Language selector

Little Java riddle - the answer

Ene, due, like…

The competition is over, the prizes were given away… And despite the trainings are still going on, it might be time to show the answer to my little riddle, and make a comment on it.

Minimal Java™ version needed to run this code is 10. There are two reasons: the presence of var and the call of List.copyOf. All overloaded variants of List.of are available since Java 9.

To answer the second question you still don’t need to run the riddle’s code, all that’s needed is careful examination of the List.of() and List.copyOf() descriptions in javadoc. (You still have a chance, the links are in the previous paragraph. ;-)) Our raz is an unmodifiable list, because the javadoc of of() says returns an unmodifiable list. It means a list to which we can’t add more elements or from which we can’t remove any elements once it’s created (because the elements can be changed “inside” if they’re mutable). What’s more, the javadoc of copyOf() states in its implementation note (yeap, we need to read that far) if the given Collection is an unmodifiable List, calling copyOf will generally not create a copy, meaning: passing an unmodifiable list results in returning exactly the same list, no copy is made. Which makes raz == dwa result in true in this riddle and therefore ene is printed. If not the else, we’d see due as well, because x.equals(x) should always be true.

Riddle run in jshell
The third question was marked with an asterisk due to a few reasons at least:

  • to answer it, one has to run the riddle or read Java sources,
  • actually it’s an implementation details and as such we should not rely on that,
  • at least two correct answers are possible today, there might be more in the future (because the point above).

When we run this code on Java 11 or never (so far up to 15 inclusively), then raz is an instance of java.util.ImmutableCollections.List12. List12 is a nested class of ImmutableCollections, and the ImmutableCollections class is visible inside java.util package, so in our code we can’t crate an instance of List12 using a constructor.
I’d like to start the analysis of List12 with its name. To my eyes (and ears) the actual name of ths call (although no-one told me that) is “list one-two”, “list twelve”. Why? Because (and now it’s worth taking a look into its source) the instances of this class are capable of keeping exactly one or two objects. What’s more, they don’t use an array or dynamic links, just two strict references, so after this list is created, it can store one element or two elements, tertium non datur. Briefly speaking, now Java also has specialised collection classes to keep just a few elements (vide Set12). As I suggested in CONTEXTVS, STVLTE! when referring to Scala example, tiny collections happen so frequently in the universe, that it makes sense to have a distinct implementations for them, also for performance reasons. Examples? Here you go: checking the number of elements inside List12 is dead simple, if the second reference is null, then we have one element, in all other cases we have two elements. Really simple and extremely efficient, no need to check array’s lenght or cache the result for subsequent size() calls.

Just for sake of completenes, a second answer is possible to the third question. In Java 10 the class List12 didn’t exist, there were two disting classes, List1 and List2. As mentions, this is an implementation detail, subject to change in the future, as it’s not a part of the public API.

Dude, so what?

This whole story has a second meaning IHMO, which I haven’t seen as any official statement anywhere, but it’s a conclusion I get when I trace the changes in each Java version since 8. My reading is that Java is slowly pushed towards languages, which also allow programming in declarative and/or functional style, not just imperative. One of the key aspects of functional style is the lack of side effects. Keeping data in unmodifiable structures helps here a lot, not to mention that it makes things easier: it’s easier to read such code, it’s easier to program in a more defensive way, etc., etc. The argument of “but the performance will suffer, dude!” can be addressed by “can’t you see how fast copyOf() works?” or “we have insanely fast classed for small, yet frequent collections”. And please don’t forget, that the hardware still performs better and better. “New” kids in the java.time block? Immutable. Records? Immutable. Streams? Single use only.

We have to go deeper

If we go deeper, we might hit another level. I use the aforementioned riddle e.g. when I start my training about the cool new stuff available since Java 8-9. When the participants hear the question “what Java version do you have”, what you often hear is a loud “ELEVEN!!”. Later, after the riddle and during the training or code review it turns out that yes, that’s true, 11 can be observed in the landscape, but merely as a JVM version running in the production, not the version we think in and develop for. It turns out that the years of education and certification, tons of outdated tutorials and thousands of ancient StackOvefrlow answers (relevant years ago) caused such serious damage to our minds, that repelling loop-osis, if-osis, outdated design patterns, mutability of everything and everywhere is really a challenging job. What’s worse, the fresh graduates of our great universities and “real-life” bootcamps are just replicas of their teachers and mentors. It’s really nice if you can spot in the new code something different from collect(toList())… And collect(toUnmodifiableList()) is really a rare gem. Therefore, my observations is this: we happily set javac to use --target 11, but our habits and minds still aren’t aiming that target. I see quite a lot of job posts like “alive JAVA 8 developer wanted”. Please don’t get me wrong, Java 8 was the version which convinced me that Java isn’t a technology, which is going to drift slowly into the dark seas of oblivion, labelled COBOL and which has to be abandoned by jumping onto Scala or Kotlin ships. Java 8 is a bit like a steam engine years ago: it can still be seen frequently, has a lot of power, etc. Maglev and hyperloop don’t make sense everywhere, but maybe the electric engines are something we’d like to see to be common on the track? Also, I don’t think that the old code is cursed, must be rewritten to modern and shiny versions, and so on. What I mean: what was good in times of Java 7, might not be good anymore. Let’s try to adapt the way we see Java these days.

… i morele bęc!!!

Language selector