Hacker News new | ask | show | jobs
by gjulianm 4835 days ago
As a C# developer now experimenting with Java, I still miss some things even from Java 8. Probably some have technical reasons behind, but...

- Getters and setters, C# style. That is, instead of

    private int foo;
    public int getFoo() { return foo; }
    public void setFoo(int value) { foo = value; }
write this:

   public int Foo { get; set; }
which is both easier to write and makes code more readable and understandable.

- Mandatory exception declarations in methods. I still don't understand why this is necessary. It ends up either leading to stupid errors (see the following example from the article)

    void appendAll(Iterable<String> values, Appendable out)
            throws IOException { // doesn't help with the error
        values.forEach(s -> {
            out.append(s); // error: can't throw IOException here
                       // Consumer.accept(T) doesn't allow it
        });
    }
, or to lots of methods just throwing Exception (bad practice, I know, but it's easy to fall in), or to useless empty try-catch on methods that won't throw an exception (because you've checked it) and don't want to add the throw declaration.

- Modifying external variables in a lambda expression. Just as everyone is used to in Javascript, Ruby, C#... It's pretty useful, even more when you're using lamdbas for common operations such as forEach.

- Operator overloading. When you're using data classes (for example, models from a database) where different instances actually point to the same object, overriding == it's more comfortable than foo.equals(bar). Also, the downside of using .equals is that, if foo is null, you will end up with a nice NullPointerException out of nowhere.

- Events! I'm amazed how easy is to do events in C# versus how hard (and bloated) in Java. Take this as an example: http://scatteredcode.wordpress.com/2011/11/24/from-c-to-java...

- Pass by reference. Just as if you were passing a pointer in C. I find it surprising how few languages implement this feature, which is pretty useful...

And I'm sure I'm missing something...

Edit: added three bullet points.

12 comments

> Getters and setters, C# style. That is, instead of

I just stopped using getters and setters all together unless I have a real use for them. The most common reasons are: because they are required by some third party library or they are the external interface for whatever the module of code is doing. For all internal classes, if the field needs external access, just make it public.

Most of the arguments I hear for using getters and setters are the result of the tendency in the Java world to over engineer everything. e.g. "What if you want to change the internal name in 8 months? or add validation years from now? or add some logic to the getter? (ignoring the fact that would no longer make it much of a 'getter')"

The only argument that holds water with me for using getters/setters everywhere, because it is actually painful, is mocking field values, but there are ways around that and the trade off in code readability with public fields is well worth it.

I really disagree that C# getters and setters are less readable than a public field. Compare:

   public string Field { get; set;}
to

   public string field;
I felt I had to reply to this comment to discourage this practice for a couple of reasons:

1) By convention anyone reading your code will think this is very strange, and it will force them to spend additional time reading your code to understand why you are breaking such a strong convention.

2) Using properties really will make your life easier when you need to track down a state change in your program. Detecting a state change on a public setter is a lot easier than detecting state change on a public field.

3) In C#, at least, using public fields where you should be using public properties will make creating an interface on existing code significantly more difficult - as interfaces can only define public properties, and not fields.

In short please don't do this. It's really bad practice just to save a couple extra characters, and if I inherit your code someday I'll probably want to strangle you.

1 + 2 are exactly why I do it. If your muddling around with the internal state of another object there should be alarm bells in your brain triggering you to think "should i really be doing this?"; It should look weird.

Hiding fields behind method calls obscures what your really doing. I like that syntax highlighting changing the color on field access and that there aren't parenthesis at the end of the call. This is what I meant by readability, I instantly know that I'm muddling around with the inner state of another object. Figuring out whats really going on with getter and setters is made far worse by the common practice of 'overloading' getters and setters to accept / return different types than the internal state representation.

3 - I would consider defining field getters in an interface poor form. There is a reason interfaces (in Java at least) do not allow field definitions. Defining a 'getter' in an interface is just circumventing that protection and a strong indication the code should be refactored in a different way.

Things consuming an object shouldn't care how it does what it asks, only that the object does it.

If knowing that something is a field rather than an opaque getter is important to the consumer, then the consumer knows too much.

You know you're messing with the state of an object when you do object.Something. That it's being messed with by a method call or a field is immaterial.

The syntax for consuming a field or a property are exactly the same. I don't see how fields ring alarm bells while properties don't.

In C#, properties are handled just like fields:

    class Foo {
       public string Bar { get; set; }
    }

    Foo.Bar = "test";
But I still don't get why wouldn't you use getters/setters or properties. For example, let's say you want to get the number of items in a list. How'd you do it without something like this

    public int Count { get; private set; }

?
I think it's partly a defensive tactic to stop others playing fast and loose with your object state.

I used to write classes and then add fields and auto generate getters/setters.

Now I don't, I just make all the state private and if I find myself wanting a getter/setter I ask myself why.

Too much use of getters/setters is just reinventing the problem that private state was meant to avoid.

So for example rather than:

if(email.getState() == EmailState.SENT) {

I have:

if(email.wasItSent()) {

What if a 3rd-party API only works on Beans? (i.e. doesn't support private properties).
With dependency injection frameworks, you don't really need setters anyway. I add getters only as needed, unless it's a public API.
2 things: 3rd-party API that only works on "beans" and thin object (not your Services or DAOs that use DI heavily).
You can't do much with 3rd-party APIs. For thin objects, a best practice is to keep them immutable by having only getters, but then you generally end up with Builder boilerplate.
The boilerplate getters and setters in java do get old very quickly, and C#'s properties handle this nicely. On the surface, it seems like something minor and superficial, but when you are seeing the unnecessary verbosity of getters and setters day in and day out, it becomes frustrating not to have something like C#'s properties.

I definitely would like to see a similar feature in java. In the meantime, I am pleased with the features in java 8 after being underwhelmed by java 7's new features.

Me too. However, I don't think Java needs much to become an appealing language (for language-geeks).

It's already going to get lambdas. Add some shortcuts for getters and setters (Lombok-style, maybe) and collections literals, then you get surprisingly close to Groovy (and JPA @Entity classes will become 1/6 of their sizes). And just like Strings have special + and +=, Bignums could have some compiler help (as they already do in JSP EL).

It will still be behind C#, but not by much. At that point I could agree that Java and C# have different philosophies and Java will never get true operator overloading, but it doesn't matter much if the most important use cases are covered.

> It's already going to get lambdas. [...] then you get surprisingly close to Groovy

The memory footprints for Java 8 lambdas and Groovy's closures are, er, quite different.

Which is better?
that is why I think java IDEs became so popular, they are almost a part of the language because they do some of this lower level refactoring for you
At least with respect to checked exceptions, my read of modern popular convention is simply to avoid using checked exceptions unless there is a very strong use-case. In our own code, we have transitioned to using predominantly unchecked exceptions with no decrease in code quality that we have yet perceived.

Agreed on assuming the trivial getters and setters if they are not expressly defined. That would be nice.

As for modifying variables in lambdas, you need to have a final reference available, meaning you can't modify a primitive. However, you can accomplish roughly the same thing by using a final reference to a boxed (or if parallel, atomic) primitive.

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.

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.

And the two things I notice Java has that C# does not (static methods on Interfaces, and functional interfaces) have completely viable alternatives in C# (extension methods, and Action/Func, respectively). This really smacks of catch-up.
Speaking of C#, it's missing one critical for me feature: it does not run on the platforms I am (or my employer is) using. There are some attempts like Mono, but in practical sense it does not. That is sort of a no go right there.
You can tweak JVM but you can't tweak CLR. While it's not Java vs C# the language but the VM/Runtime and the language is almost tight together (Mono is an exception).
What I'm missing the most from C# is actually the "var" keyword.
The checked exception concern leaves me scratching my head. Without checked exceptions, the forEach implementation has no way to gaurentee that the lamba won't throw, and has to be exception-safe at all times -- in the case of stateful code, this can generate a lot of boilerplate.

Rather than write all that boilerplate, most people won't actually write exception-safe code across the board, which allows strange state-dependent failures to creep into the system.

The unexpectedly-unwound-stack problems that Java's compiler is highlighting are still there, just hidden.

Better, in my mind, to not use unchecked exceptions at all.

It's a pain that operations can fail, but handling state when they do fail is a problem that exists whether your exceptions are checked or unchecked. Forcing all exceptions to be checked doesn't seem to help with the problem, but would rather cause a lot of code to simply declare that it throws Exception, rather than listing off all possible exception types that it doesn't handle (e.g. out of memory, SIGTERM received, SIGINT received, etc)
Failure types are generally classifiable; part of clean module design is in classifying your exceptions in a way that allows you to declare that a more or less granular exception types are thrown based on the actual failure mode(s) possible from your code.

The 'throws Exception' behavior only emerges when people don't take the time to define how code can fail, which is something that is necessary to fully define an interface, regardless of whether or not you have language support for declaring failure modes.

Given that most code degrades into either wrapping checked exceptions into runtime exceptions or most methods having "throws Exception" declared...I think it's safe to say Checked Exceptions don't work well in the real world. There is a reason no other language created in computer history uses them.
> Given that most code degrades into either wrapping checked exceptions into runtime exceptions ...

This is a self-referential argument; people who are disinclined to take the time to define how code fails are disinclined to define how code fails.

The code can still fail, the failure cases still must be defined, but now the compiler won't warn them that they've failed to do so, and the result is that the failure cases are undefined and unknown, and the code is less reliable.

Without defining failure modes, the system is always in an unknown state: any undefined failure will leave any any stateful code in an unknown state. Since failure modes must be defined, either in code, or in documentation, and writing documentation takes longer, and it must be maintained (and read) manually.

Since defining failure modes manually takes more effort than using the compiler's support for checked exceptions, the reality is that unchecked exception advocates are really arguing for not defining failure modes at all; this is simply laziness.

> There is a reason no other language created in computer history uses them.

Many languages simply don't use exceptions at all, since having what amounts to an 'atomic goto' isn't necessarily considered an advantageous feature. Not using exceptions is the only choice that doesn't introduce a significant likelihood of program failure due to littering code bases with what amount to unchecked stack unwinding gotos.

That said, your statement is not really correct. Languages like Haskell provide checked exceptions via library features. C++ has an optional checked exception mechanism, but they aren't enforced at compile-time (unfortunately).

Forcing all exceptions to be checked doesn't seem to help with the problem

I think this is a common misperception of Java - not all exceptions needed to be checked exceptions. The author makes a decision when defining the Exception class if it is going to be a checked exception or unchecked exception.

You might like project lombok re: getters and setters. It has a feature that implements this using annotations. True, this is an additional external dependency for a project when it'd be nice if it was a core language feature, but as external deps go it isn't a killer one -- the library is not huge and doesn't spiderweb out to a bunch of other deps.

http://projectlombok.org/features/GetterSetter.html

(There's also a lombok-pg project that offers more extensions.)

We actually evaluated Lombok for this reason (and others), and eventually decided against continuing with it for a few reasons:

* Every team member must install the eclipse plugin (which, if you already have any eclipse vm args set, also requires you to add a couple of extra arguments).

* No reasonable way to add javadoc comments to getters and setters without explicitly writing those getters and setters (kind of defeating the purpose of lombok).

* Sonar (which we use for static code analysis) will now report those private fields as a major warning as it thinks they are unused private fields. You can make those warning go away by adding //NOSONAR on each line, but yeah..

* Relies on internal java APIs. While the authors seem to be quite good and stay on top of things, I fear that this is still inviting issues down the road.

We felt that the cons outweighed the pros in this case, despite some of the beauty in lombok.

Disclaimer: I am a colleague of the author of the linked article.

Give the desire for lambdas to enable more simple concurrent programming, and the added complexity mutating the external variables in the implementation, I think they made the right choice.

If you _really_ have to mutate those variables then put them into a final array, and alter the entries in that, the same can be used if you need to do an effective pass by reference.

The Atomic* classes are also good choices.
I like my checked exceptions. I like knowing what might go wrong with a call and I like being forced by compiler to handle it.

True that in typical chaos that reigns in code written by a disjointed group of people it ends up being Exceptions and RuntimeExceptions all over the place anyway. But why should disciplined people suffer?

Please leave my checked exceptions alone. I like then very much. I have not dealt with C#, but did with C++ and I prefer the Java way.

The arguments against checked exceptions that I have seen go along the lines of leaking data types from unrelated code layers. That's probably valid, but I have not experienced it. The rest seems like just whining about being forced to write code to handle errors, instead of letting things just happen.

To me, checked exceptions seem to be a second return value: you need to catch it or declare that you are going to pass it to the caller. In this sense, it is enforced by the compiler even more rigidly than the return values themselves. They can be useful.

OTOH, sometimes we need a "panic"(¹) function, and a main loop that catches everything and either communicates the error to the user, or restarts the whole thing. That's why unchecked exceptions can be useful too, especially since they makes it harder to ignore errors - who has the patience required to check every return value of printf in C?

(¹) No, I've never programmed in Go.

>- Getters and setters, C# style.

In Java, I use Lombok:

@Getter @Setter private Integer foobar;

>Checked exceptions

True, widely regarded as a mistake, but hard to get rid of now.

About events. One could argue that the example code is really made to look bad in the Java example.

Sure, it's easier in C#, but there's no need to invent problems that doesn't have to be there.