Is Java Dead?

java | Comments comments

The answer is: of course not!!, but now that I got your attention surely due to the title that mimics the lately hot “Is TDD Dead?” discussion started by @dhh, I would like to concentrate on the real objective of this post that I think it is really more important: is Java taking the right road? or in other words, are the last changes in Java 8 “good” ones or not?

So, let me start saying why I think Java is not dead, to stop any language fight from the beginning even though the reason is a no brainer. It is not dead because despite how old Java is becoming and how it is starting to show its wrinkles through the lack of type inference, numbers not being objects, etc., it is still widely used, it has a big and productive community and I think that almost for sure, its community is the one that more open source projects has produced. A language it is not only used by its technicals features but also for the community it has, the projects it supports, etc.

Having said that and made that clear, let’s now concentrate only on the technical part. So, are the changes of Java 8 “good”? Are they useful? How can we measure if they are “good or bad”? The plain answer is, it depends, it always depends :–). But, it depends on what?. A few months ago after a talk I gave at the Scrum Gathering Bolivia, an attendee came to talk to me because I made some comments about Python’s issues. He told me: “I’m really happy with Python! I think I’m more productive that I used to be with the language I used before”. I asked him which language was that, and he said: “Java”. So yes, it depends. If you are used to program with Java, suffering due to its archaic type system, the lack of closures and its verbosity, Python looks like the holy grail, but is it?

Programming languages reflect the knowledge, beliefs and ways to solve problems the authors of those languages have. But not only that, they carry on their shoulders the heavy weight of its history and that is, as I understand, what it is killing Java, or at least what it is not making Java get any better when you compare it with other languages.

Of course that for Java programmers, version 8 has many interesting features, but from the point of view of other programming languages, how “good” are they? Are these changes making Java a “better” language or just keeping it from its (in)evitable dead end? I propose you to analyze some of the Java 8 changes from both perspectives and see where we get.

New Date and Time model (so called API)

It is about time to throw away the ugly, full of design errors, best example of all the things you don’t have to do, Calendar class. That class contains almost all the design flaws you can look for, like a misleading name because its instances do not represent calendars but dates, or dates with time, or… wait a moment, another problem, it does not represent just one thing but many! it does not follow the “Single Responsibility Principle” and not only that, it allows you to represent invalid dates as February 31st of 2014, or change dates when dates should be immutables like the numbers and many other objects. You have no idea the amount of people I interviewed with the misconception of what a Date is and what a Calendar is due to this really bad abstraction (all Java programmers).

So, the new date/time model is “good”, it is an advance towards less misunderstanding, it provides new nouns that makes the language grow and allows us to talk and think about days of month, like December 25th (with the abstraction MonthDay) or months of year as April 2014 (with YearMonth). Finally, with the new time model not everything is a Calendar!.

We can also express “measures of time” with a Duration and even Years! Yes! they broke the fear of “optimization” that makes most programmers represent years with integers! We all know that a number is not a year, we all know that a number does not respond to isLeap but it makes a lot of sense for a Year to answer that, so I toast for this new time model that prioritizes good abstractions over performance/space.

Yes, this model is “good”, but is it? Let’s see how it compares to a date/time model of other languages. In this case the one implemented in Pharo (an open source implementation of Smalltalk) called Chalten. (You can read about it in this paper published in 2005). With Chalten you can not only represent those elements that you can with Java 8 but also many more, starting with real measures of time because it uses an algebraic model that allows you to represent any type of measure (Aconcagua). With Chalten you can represent 1 day, 3 months, 5 year, 7 centuries and any other kind of time measure like 2 semesters (if you create the unit semester). There is not only a Duration that it is limited to the time units it provides through messages like toHours, toMinutes, toNanos, etc. but the possibility to create any time unit you need or want.

Chalten is based on an analogy that sees Time as a line of different granularities where you can zoom in and zoom out, and therefore you can represent intervals in those lines, vectors and segments of any granularity and apply the common well known operations like intersection, union, iteration, etc. on any of them.

With Chalten you can represent relative points in time like “20 days from now” or “3 months since April 2014”. Those relative points can be filtered with the filtering rules of a “calendar”, as a labor calendar that defines Saturdays and Sundays as not working days, and therefore “20 days from now” will not be the same applied that calendar that to another one where Saturdays are workable days. These are, to name a few, features Chalten has since 2005 that the new Java time model lacks.

So, is the new java date/time model “good”? It is better than the previous one. Is it the best one? Sadly it is not if you compare it to other long ago known solutions. But it definitely does not hurts you like other “new features” of Java 8.

The so waited Closures… wait Lambdas!

Let’s start making clear that Closures are not the same as Lambdas, not even Full Closures. The definition of these concepts will vary depending on the literature, programming language, etc., but mainly the difference is how they bind. With Java Lambdas you can not change variables of the current lexicography context, where with Closures like the ones you have in C# you can, but you can even do more with Full Closures where the return also binds to the current context as in Ruby and Smalltalk.

So, are Java Lambdas “good”? Well, definitely they are better that the freakish anonymous classes because all the boilerplate to use them is gone, but in the end are almost the same. The Java lambdas are mainly syntax sugar to avoid using certain kind of anonymous classes and a minor (good) change on how they bind.

Java lambdas will allow you to code better, to create better abstractions, to reduce “lines of repeated code” but still, Java lambdas are not the “best solution”. Full closures would be better as proved by Ruby, Smalltalk, Scheme and the famous “Lambda: The ultimate x” series of papers written of the last half of the 70s… yes, a long time ago.

Why Java 8 does not have Full Closures or at least Closures? Definitely not because they are not good, definitely not because they are difficult or very hard to implement, Ruby has them since its creation circa the same year as Java, Scheme has them since the mid 70s and Smalltalk too.

Null considered harmful

If I took @dhh’s “Is TDD Dead…” phrase, why not used the so famous “x considered harmful” derived from Dijkstra’s well know “Goto considered harmful”?

I see with great joy that we are starting to realize that null is not a good idea. We are starting to see more and more languages that are trying to provide a solution to all the problems null provokes. Even the newly Apple’s Swift provides some aid allowing variables to reference nil or not, and send messages with a “?” at the end that helps avoiding to check for null/nil on each message send.

What is Java 8 doing about it? It provides an abstraction called Optional<T> that responds to messages like ifPresent (encapsulating the hated if x==null), orElse(T anObject) that returns the wrapped object or the parameter anObject if the wrapped one is null, and some others.

At first sight looks good, it is an abstraction that encapsulates all the if x==null we are forced to write if we want to produce robust software. Looks like a step forward, but is it? Let see what other languages have, for example Ruby.

Let’s start pointing a big difference. In Ruby nil is an object, not a “reserved word” that the compiler recognizes and treats specially. So because nil is an object it can answer messages like nil? that encapsulated the if x==nil and many other you would like. The same in Smalltalk, nil responds messages like ifNil: that encapsulates the if anObject isNil then ... in just one anObject ifNil ..., and ifNotNil:, and ifNil:ifNotNil:, etc. All very handy messages and all the new messages you may need because nil is an object and therefore you can extend its protocol.

So what is the difference? The difference is that in Ruby and Smalltalk you don’t need a special abstraction to handle null, it is just it!. There is no need for an Optional<T> abstraction to wrap an object and respond messages you would like null to answer.

With time, we will start to realize that the Optional<T> class has the same issues that the Integer class, the Long class and all the wrappers of the data types used to represent numbers in Java because numbers are not objects. It looks like we have learn nothing from the mistake of not having objects all the way down. Let’s see some examples starting to interact with the wrapped object.

1
2
3
4
...
Optional<String> aString = Optional.of("something");
aString.charAt(1); <-- It does not compile because aString's type is Optional
...

As you can guess, the example does not compile in the line 3 because aString is not really a String but an Optional. So we have to send the message get to aString to get the real String (does it sound as a tongue-twister? I promise it is not my purpose :–) ):

1
2
3
4
...
Optional<String> aString = Optional.of("something");
aString.get().charAt(1); <-- Now it works because we are getting the wrapped object
...

Therefore, everytime we want to access the “real” object we need to send get to the optional one. So if we want to send many messages we have to:

1
2
3
4
5
6
7
...
Optional<String> aString = Optional.of("something");
aString.get().charAt(1);  aString.get().charAt(1);
aString.get().charAt(2);
aString.get().charAt(3);
aString.get().charAt(4);... etc
... etc

To avoid sending that many get, we could just do:

1
2
3
4
5
6
7
8
9
...
Optional<String> aString = Optional.of("something");
...
String realString = aString.get();
realString.charAt(1);
realString.charAt(2);
realString.charAt(3);
realString.charAt(4);
... etc

But if we do this, why are we using Optional for? We can make a mistake and assign null to realString and get back to what we wanted to avoid in the first place. So Optional does not really stops programmers to send messages to null.

Now let’s go a little bit deeper. A common use case is to stop execution and return from a method if a variable references null, like:

1
2
3
...
if (aVariable==null) return;
...

How does the new abstraction help us in this case? Let’s see, it would be great to write something like this:

1
2
3
4
...
Optional<String> aString = Optional.ofNullable(null);
aString.ifNotPressent({return;});
...

But it is not possible because there is no message ifNotPresent in Optional! just ifPresent. Let’s see how we can do the opposite with ifPresent. Let’s say I want to return if the variable is not referencing null:

1
2
3
4
5
...
Optional<String> aString = Optional.ofNullable("something");
aString.ifPresent(aNotNullString ->{return;});
System.out.println("got here");
...

What do you say? does it get to “got here”? :–) or does it return from the method where this code is? Sadly it gets to “got here”, it does not return as one would expect because lambdas are not full closures! That return exits the lambda but not the method where the return is written.

So, how do we get out of this mess? How can return from a method if an Optional is wrapping null? Writing:

1
2
3
...
if (!aString.isPresent()) return;
...

Please, you tell me the difference with:

1
2
3
...
if (aString==null) return;
...

What have we gain? Only more complexity. Are there any alternatives? How do other languages treat this problem? In languages with full closures where null is an object like Ruby or Smalltalk, you could just write:

1
aString ifNil: [ ^something ] (the ^ means return)

And that is all folks, simple, direct and does what you expect, if aString references nil returns “something”.

So, not only the Optional abstraction is a halfway solution (a fullway solution would be to make null a first class object) but the lack of full closures do not really help much here, on the contrary.

To end this section, I would like you to think a little bit more about this problem. Is there another solution besides making null a first class object and have full closure to solve this problem? I think there is. I think the best solution is to get rid of null/nil completely. Not even the “?” of Swift or C#. The truth is that we do not need null/nil, it has so many meanings, we use it so badly, it is so error prone that the best solution would be to remove that idea completely. So instead of null/nil we would have an abstraction called UninitializedVariable to indicate that case, another called NoSuperclass to point out the a class has no super class, or EndOfList to indicate the we got to the end of a linked list or UndefinedAddress to represent the fact that the user did not specify his/her address and so on. Think about it for a moment, you may like it :–) but if not, watch Tony Hoare’s opinion about “Null References: The billon dollar mistake”

Conclusion

Are the Java 8 changes “good”? Some of them yes, the date/time model is definitely better of what we had, the way to represent code with lambdas is definitely better that anonymous classes but sadly not good enough, and the Optional abstraction does not solve the real problem but adds complexity. Some of these changes are good if you are a Java programmer but some are not, even if you are a Java programmer, because they add unnecessary complexity, and if you are not a Java programmer, most of them are still ancient code, like for Ruby or Smalltalk programmers, even for people that work with C#.

You may wonder what it is the goal of this post. Is it to talk/promote Smalltalk or Ruby? Is it to show that I don’t like Java? Not really. I wrote it because I’m afraid that for people that only work with Java these changes may look great, a step forward a better life. My fear is that those people may think that the only way to treat null is with Optional because all the know is “the Java way”, they only live in the “Java world”. My fear is that I will now start interviewing people that instead of confusing Dates with Calendars will confuse Closure with Lambda, people that will not see that instead of hiding the problem under the carpet as with Optional, we should get rid of the real dust.

So, I wrote this post in an attempt to open programmers mind, to show them that there are other places too look at and learn, even if you make a living from Java. If you really love Java, the only way to get a better Java is forcing real, profound changes on it, not just changes that keeps back-compatibility.

Will Java ever make profound changes on it? Will Java take better paths? I think it will not. The back compatibility principle they apply is stopping Java to evolve better. We will never see Java without null or at least a first class object null, we will never see Java with a better type system, we will never see numbers as objects in Java; its back compatibility does not simple allow it.

I would like to finish with something Alan Kay said about Smalltalk, its creation, when discussing the future of the language with his group composed by great minds like Adele Goldberg and Dan Ingalls among others. Basically he was not happy making Smalltalk a commercial language because he knew that after releasing it, Smalltalk would stop evolving because the priority would be to satisfy users/customers needs (backward compatibility among them) instead of creating new features and experimenting new ideas.

In other words, when a programming language hits the market its future is defined.

Comments