Hacker News new | ask | show | jobs
by Hermel 4837 days ago
I don't think there are technical reasons behind, but rather philosophical ones.

- Getters and setters are bad design. In clean object-oriented code, the internal state of a class is not exposed to the outside. Thus, getters and setters should not be encouraged.

- Exceptions: yes, it would be nice to be able to throw checked exceptions in that example. But the rule that they need to be declared is one of the basic design decisions Java has taken with the intention of always forcing the programmer to handle them. It won't change. (You can still fall back to unchecked exceptions if necessary.)

- Modifying external variables: can be done with a work-around, e.g. "final int[] x" to have a modifiable int. Not sure why that is the case though.

- Operator overloading: considered bad practice for high-reliability code. Generally, Java favors explicit statements in order to prevent mistakes.

- Events: some of the listener stuff in the example could be moved to a library, making it smaller. But still a missing feature in Java.

- Pass by reference: this is normally used as a workaround to create methods with multiple return values. In Java, one could again use the wrapping-array-workaround instead as in the external variables point. I'd love to be have methods with multiple return values in Java.

5 comments

Operator overloading: considered bad practice for high-reliability code. Generally, Java favors explicit statements in order to prevent mistakes.

Considered by whom?

The example gjulianm gave, where == is safe if nulls are flying around but calling .equals() on a null expression results in an exception, seems a clear counterexample.

I’d also argue that if your code is mathematical in nature, it is both quicker and more reliable to write, review, and maintain concise, mathematically natural syntax like a+b*c than verbose alternatives like a.add(b.multiply(c)).

Considered by people who have used C++ and seen some awful problems creep in because of unnecessary operator overloading.

Your last point actually highlights the problem - it is not more reliable because you are assuming what "+" and "*" do.

EDIT: An old blog post about the issue: http://cafe.elharo.com/programming/operator-overloading-cons...

Your last point actually highlights the problem - it is not more reliable because you are assuming what "+" and "" do.*

How is that any different to assuming what add() and multiply() do?

In many ways, I’d say the latter is worse, because it’s not immediately obvious from just the function names whether the operation mutates the object it’s called on or returns a new object with the result while the originals remain unchanged. Both behaviours can be useful. Both are widely used in libraries, with those exact function names. You just have to read the documentation and learn how each library you use works, which is particularly... entertaining... if your project uses multiple libraries and they don’t all follow the same convention.

For + and * operators, someone already decided what the convention should be. If you just make them work like the built-in versions, the semantics are already intuitive to anyone who studied basic arithmetic at school.

Edit: I did take a look at the article you cited. It appears to be an elaborate troll, actually consisting of little more than a set of unlikely examples (I wouldn’t write a function called divide() to “divide” two matrices, so why would I suddenly feel the urge to write a / operator?), a set of assertions without proof (some of which are just begging the question), and a final ad hominem that conveniently discards one of the most obvious demonstrations that operator overloading can be useful on the basis that anyone making that argument just doesn’t understand (and ignores counterexamples like OCaml, where basic arithmetic operators really are different for basic numerical types, which is a significant pain point in the language).

> For + and * operators, someone already decided what the convention should be. If you just make them work like the built-in versions, the semantics are already intuitive to anyone who studied basic arithmetic at school.

I think it's exactly this that is misleading - your intuition may not be the same as developers. I think people assume more from "+" than they would from "plus()" and if you don't think about it you may falsely assume you are using a language built-in.

I'm not saying that operator overloading can't be useful, but in C++ developers did create horrible code by using /, *, + in their classes to 1) show off that they could and 2) to save typing. If they had to name their methods, I hope there is a good chance they would have done better than divide(), multiply() etc.

Regarding the article, I agree it is somewhat guilty of hyperbole, but I don't think calling it a troll is remotely fair.

BTW I don't understand what you mean by "can be useful on the basis that anyone making that argument just doesn’t understand", which makes me feel like I've wandered into a double joke ;)

Fair enough, perhaps calling the article a troll was a little harsh, as I don’t know how the author intended to come across. However, the article did seem to consist of one weak argument after another: an unlikely strawman problem here, a blatant exaggeration there, a quick logical fallacy to tie them all together. I’m afraid I didn’t find it a convincing case at all.

BTW, I meant my final comment to be parsed as (conveniently discards one of the most obvious demonstrations that operator overloading can be useful) (on the basis that anyone making that argument just doesn’t understand). The author didn’t actually explain why that case was “completely different” or what someone who proposed that example didn’t understand so that “their opinions can be discounted”.

In fact, I would suggest that his entire case about built-in operators being unambiguous even though they support multiple types can be annihilated using only the equality operator and the words “floating point”: in C, you still can’t tell whether the expression a==b is reasonable or almost certainly a bug without knowing the types of a and b, which AFAICS is exactly analogous to the problem the author is so keen to attack with operator overloading.

>> natural syntax like a+bc than verbose alternatives like a.add(b.multiply(c)).

> it is not more reliable because you are assuming what "+" and "" do.

You also assume what "add" and "multiply" do.

Yes, but I think it is far worse in the case of built-ins; it is explicitly obvious that "add" is not part of the language, which is not the case with "+". Admittedly this is much more of a problem for new programmers than experts.

To be fair, I think a lot my prejudice against overriding operators is summed up by Bruce Eckel (who explains why it is arguably misguided in the case of Java):

"[Java designers] thought operator overloading was too hard for programmers to use properly. Which is basically true in C++, because C++ has both stack allocation and heap allocation and you must overload your operators to handle all situations and not cause memory leaks. Difficult indeed. Java, however, has a single storage allocation mechanism and a garbage collector, which makes operator overloading trivial -- as was shown in C# (but had already been shown in Python, which predated Java). But for many years, the partly line from the Java team was "Operator overloading is too complicated."

http://www.artima.com/weblogs/viewpost.jsp?thread=252441

     In clean object-oriented code, the internal state of a class is not exposed to the outside.
One could argue clean OO code also follows the uniform access principle. I guess everything being a method is uniform. In which case why are public fields allowed?

The verboseness of writing getters and setters doesn't realistically stop people from writing them. It doesn't educate them as to why writing lots of them might be a bad idea. It doesn't suggest a better design. It just makes code harder to read and people put up with it, occasionally grumbling to themselves.

It's this kind of philosophy that's prevented java from having a terse lambda syntax for so many years, which in turn has left it out in the cold as API tastes have changed.

> In clean object-oriented code, the internal state of a class is not exposed to the outside.

So then you don't write getters and setters for that stuff in C#. I don't get your point here.

The point is that a language should not add features that make it easier to write bad code.
The point of getters and setters is to avoid exposing internal state to the outside. In this way you're not getting direct modification of fields, you're sending a signal: "hey Date object, get me your representation as an integer number of seconds since epoch", the outside doesn't care whether the Date internally uses the seconds since epoch representation, or whether it translates to this representation just for this method.
Getters / Setters don't automatically mean that the internal state of an object is exposed. For example, an object may internally have mutable linked-list of something, but a Getter that returns an array (immutable) of immutable members.

What is the alternative to Getters / Setters (or, properties, which is basically just syntactic sugar) in an OO language?

That's being way too dogmatic.

The problem with taking encapsulation to an extreme is that you are trading off against flexibility. The more you hide your state the less any external party can implement their own logic using that state, and the more separate concerns have to be bundled into the class hiding the state because they can't be dealt with anywhere else. Everything in software design is a tradeoff.

I wonder what those dogmatic programmers do when they are confronted with a problem in the real world where they need to find a balance between "encapsulation" and "single responsibility".

I guess their brains simply deadlock.

> Getters and setters are bad design. In clean object-oriented code, the internal state of a class is not exposed to the outside. Thus, getters and setters should not be encouraged.

How can this be a general statement? Some state can be internal, some can be public. Java supports both. Or how do you suggest that state should be accessed? Because I don't understand how a system can work if no state is shared between objects.

At least one paradigm, is that you ask an object to do something for you with its internal state and return the result, instead of getting its internal state and doing the operation yourself. And instead of setting state directly, you have methods that let an object know something happened, with data optionally associated with the event. This paradigm really decouples objects, which is good, but is fairly time-consuming to pull off, and doesn't necessarily lend itself well to being extensible. I generally utilize this paradigm for mission-critical business logic. If I'm doing something UI-related on the other hand, my classes are festooned with getters and setters just because I need to iterate fast and get it done.
> Thus, getters and setters should not be encouraged.

Damn, so why are they encouraged in Java? 11 out of 10 frameworks insist that you write getters/setters so properties can be accessed from outside.

Java designers need to get more practical. If getters/setters are unavoidable, I'd better have them without so much boilerplate in my code.

No need to go the C# way. Just enough to avoid repeating the name and type 3+ times each would be great.