What's wrong with Java?

There’s already a rant about Java in a previous post, in which I basically list the annoyances that the ecosystem around Java imposes and how those relate and intersect with the problems of IDEs.

Here I’d like to talk about the issues of Java as a language. Although these are a significant part of what’s wrong with Java, keep in mind that it’s only half of the equation, the other part being its surroundings.

Pointers, Pointers everywhere

Java is supposed to be a pointers-free language, unlike those pesky C and C++. Pointers, although very powerful, are a low level construct that should not be present in a higher level language. Most of the time We want to be as far away as possible from pointers when programming unless lower level memory access is specifically needed.

The problem is Java does have pointers; Moreover, it manages to keep most of the inconveniences of having pointers while giving none of the benefits of not having them. If you’re not giving me the power of pointers, at least be kind enough to remove the problems they induce.

Everything is a reference, everything is a pointer

Java loves to call pointers by the nickname of “references”, which is only a way to pretend that there are no pointers.

Java makes everything a pointer, thus the heavy usage of the new keyword as a way to create a reference. Having this references gives the JVM the ability to manage memory with the Garbage Collector of course, but it comes with negative consequences for the programmer.

NullPointerException

The book Elegant Objects by Yegor Bugayenko says:

In a nutshell, you’re making a big mistake if you use NULL anywhere in your code. Anywhere – I mean it.

And I completely agree with that. The problem here is having to take into account the possibility of NULL in a high level language that supposedly doesn’t have pointers and tries to hide those details from you in the first place.

In C or C++, when you dereference a NULL pointer, you get a Segmentation Fault and your program crashes. In Java, when you try to use a NULL reference you get a NullPointerException and your program crashes as well. So what gives?

You may say that the sources of these crashes are different, the Segmentation Fault comes from the OS trying to stop you from crashing the entire system, while the NullPointerException comes from the JVM that… Well, has nothing left to do but crash. I don’t see how is that any better.

NullPointerExceptions are terribly common in Java, and you have to hunt them down just as any null pointer dereference bug. And if you’re thinking the actual benefit of this is having the GC taking care of the memory instead of having to remember to manually free memory, then think again, as there are languages that take care of that without a GC, including C++.

Tony Hoare himself calls NULL the “Billion-Dollar mistake”.

Useless pointers

So java is cluttered with pointers, useless pointers. In C/C++ pointers are one of the most powerful constructs, they allow you to get closer to the machine and control its actions with scalpel precision; In Java you get your programs to crash due to NULL pointers while getting no benefit in exchange.

Pointers in Java percolate up in even more creative ways, take for instance the Equality comparison problem: When you perform equality comparison == what you’re actually comparing is pointer equality, not value equality for which you need a special method equal(), this is a low level language trait that, unlike other low level languages, won’t put the power on the programmers hand but just the burden.

The bad, the worst and the ugly

Java has a lot of additional traits that make it not only a low level language in disguise, but also in my opinion a bad language in general.

Awful Verbosity

Most of the Java ugly verbosity is attributed to its static, strong typing discipline that forces you to annotate the types of everything, everywhere. But this is not the type discipline fault.

In Java, you declare, for instance, a vector of integers:

Vector<Integer> vector = new Vector<Integer>();

Or a vector of vectors of integers:

Vector<Vector<Integer>> vector = new Vector<Vector<Integer>>();

And it gets progressively uglier like that. Java’s way of dealing with this to some extent, is the empty diamond operator <>, so instead we could write:

Vector<Vector<Integer>> vector = new Vector<>();

But that’s pretty much as far as it gets. C++11 on the other hand has the auto keyword to let the compiler do what compilers do best: mechanical, repetitive, deterministic tasks; Type inference is one of those tasks.

Every time, everywhere a time annotation is needed, you provide one only if it’s necessary to avoid ambiguity, otherwise, just use auto and let the compiler do it for you.

Resource un-safety

One of the main Java selling points is Memory Safety, you see, in C you have to free your memory with free() in the right place, at the right time after every memory allocation with malloc() and friends. If you forget to free your memory you’ll have memory leaks, if you free it twice, or if you free it at the wrong time you’ll have a segmentation fault.

Java on the other hand leverages the Garbage Collector to do it for you, the problem is, this works for memory only!

Whenever you initialize a socket, or a database connection, or open a file, you still need to close it at the right time; So you still can and will have resources leakage.

C++ solves all of those problems beautifully by using Resource Acquisition Is Initialization or RAII for short. And by the way, if you hit the same kind of problems you face in C with malloc() and free() but with C++’s new and delete, then you’re doing it wrong.

By using C++’s RAII mechanisms you’ll never have to remember to free memory, close files, sockets, database connections or anything else. Java is supposed to be a higher level language than C++.

Exceptions Driven Programming

C++ and many other imperative and OOP languages suffer form the Exceptions driven programming issue as well, but Java manages to screw it up even further.

The heavy use of exceptions forces the programmer to write tons of:

try {
    ...
}
catch(someExcetption e) {
    ...
}
catch(someOtherExcetption e) {
    ...
}
catch(yetAnotherExcetption e) {
    ...
}

The usual alternative is just:

try {
    ...
}
catch(Excetption e) {
    System.out.println("An exception has occurred, sorry ¯\_(ツ)_/¯");
}

The result of this is that the code that matters, the actual logic we’re trying to encode in the program gets deeply buried, making it hard to read, hard to understand, hard to maintain, hard to modify and awfully ugly. Although most languages suffer from a variant of this issue, some other languages handle it gracefully by encoding the possibility of failure in the type system.

Most programming languages break equational reasoning, but that’s pretty common; Exceptions go further by even breaking the imperative sequentiality (cough GOTO cough).

Everything is an object

As mentioned in a previous post: No, not everything is an object. OOP has a lot of problems on its own, and it deserves its own post, but here I’m talking about the way Java enforces OOP.

Most OOP languages have this paradigm as a feature, but still allow for free functions, free data and so on. The problem with Java being strictly OOP is that it forces objects even when they don’t fit, even when they adversely affect composition, modularity or readability.

The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle. – Joe Armstrong

In most languages you can perform actions, but in Java, having objects as the only mean of abstraction you must have “actioners” to perform actions, and must force them into existence to do anything even if it convolutes your code and logic. OOP is usually bad in general, although useful in certain contexts; Java makes it so that everything that is wrong with OOP is also the only way.

Those and more are the common pains of Javaland, that Steve Yegge describes wonderfully in Execution in the Kingdom of Nouns.

Performance

Java is both fast and slow, depending on what language you compare it with. When you compare it with higher level languages, Java is reasonably faster, but when you compare it with C or C++, Java is miserably slow and heavy on resources.

Taking into account that Java is more of a low level language rather than a high level one as we have seen, it should be compared to its closes cousins C and C++, in which case you inevitably conclude that it’s just slow, very slow.

Java sits in a dead spot

As we’ve seen, Java is mostly a low level programming language that doesn’t really provide the benefits of one, while it pretends to be a high level language and fails miserably.

This leads to the current situation:

| C | C++ | Rust | Java | Ruby | Python | PHP | Perl | Earlang | OCaml | Haskell

|--- Low Level --| ???  |---                  High Level                    ---|

Java is a bad low level language

From the low level languages extreme, Java can perfectly be replaced by C++, Rust and others. Both of these languages provide low level capabilities, and are good for systems programming, while providing better high level traits like C++’s RAII or Rust’s statically guaranteed safety. Both of these languages will avoid Java’s NullPointerException and resource leaks.

Java is a bad high level language

From the high level languages extreme, Java can be replaced by pretty much any other language. Almost any of them will provide a nicer syntax, better and more powerful ways of abstraction, better terseness, better tooling, better everything.

This makes Java completely replaceable by any other language, it serves no particular purpose and is particularly good at nothing.

Bad programmers abstractFactory

Professors in computer science Robert B.K. Dewar and Edmond Schonberg, published an article in the “Journal of Defense Software Engineering” discussing how Java is a bad programming language for CS education, and how it produces programmers that are incapable of doing actual problem-solving. Or, as Joel puts it in his article “The Perils of JavaSchools”:

I’ve seen that the 100% Java schools have started churning out quite a few CS graduates who are simply not smart enough to work as programmers on anything more sophisticated than Yet Another Java Accounting Applications