Hacker News new | ask | show | jobs
by piaste 2047 days ago
It's entirely about backwards compatibility and commit cleanliness.

Say you have an int field and you want to change it to an int array field. To support existing code, you add a method that returns the first element of the array (let's assume it's guaranteed non-empty for simplicity).

If you used a field, you have to update all code referencing that field to call the accessor method instead. If your class was part of a public API, it's a breaking change.

Even if it was just internal code, your next commit will contain a zillion changed files where you replaced foo.number with foo.getNumber(). You'll leave a trace in each of those files' histories for a change that really didn't concern them at all. Also code reviewers will have to check that you didn't miss a usage somewhere (hopefully your code would just fail to compile in that case though).

If you had used a property or a getter around the original field, none of that happens. You add a new .Numbers property and replace the old .Number implementation with one that gets or sets the first element of the array.

1 comments

> Say you have an int field and you want to change it to an int array field.

Will you ever do that in real life? Seriously, why would someone change something from an int to an array...

I see all these Java programmers say that you should always use getters/setters and not expose public field in classes for this reason, yet I have to see an example where this would have happened.

Also the argument that you will need to touch more files in a refactoring. Sure. Not doing so would mean adding a technical debt, a method that exists for the sole purpose of some code that you were too lazy to update. Do that and your software will become spaghetti code easily.

I see this typically Java code that is not readable for all these getters, to the point that a simple class that represent a user is like 200 lines of code long for all these getters/setters that only return or set fields of the class without doing nothing.

Is also a matter of performance (no wonder Java code is that slow), since you are calling a function for each property access (and a function call is an expensive operation, it involves jumps, you have to access the vtable for the object, set up the stack frame) where you could simply access a field in an object (a simple addressing with an offset for the CPU). Sure, a call is not that expensive. But imagine doing that in a loop and it will be a performance problem. It will make also the CPU optimization work more difficult, since you are jumping around.

> Will you ever do that in real life? Seriously, why would someone change something from an int to an array...

This happens quite often, when one-to-one relationship is replaced with one-to-many. In this specific example it could be an integer identifier of an object. If you need a real life example, think of an item in online shop, which had a category assigned and you now want to assign multiple categories.

On a side note, nothing I said justifies the use of properties. Refactoring is heavily automated today and users of an API undergoing such a dramatic change would have to review all uses of this value anyway. Having broken build would guarantee that this happens.

> Is also a matter of performance (no wonder Java code is that slow)

Surely these would be trivially inlined?

All public non-final methods in Java are virtual, so it is not _that_ trivial. You would have to check all subclasses that might get passed as an argument and check that again whenever new classes are loaded into the JVM.
JVMs are quite smart about this sort of thing, and a nonfinal method can still be inlined, even though the JVM has to safely undo that optimization if a class is loaded. I'm not sure where I learned this originally, but Aleksey Shipilev has a post on method dispatch that should explain it. https://shipilev.net/blog/2015/black-magic-method-dispatch/
It's exactly what the JVM does, it's called Class Hierarchy Analysis (CHA). The VM checks if a method is actually overridden or not.

When a new class is loaded, the analysis checks which codes are no longer valid and mark them for deoptimization.