In a previous post, I drew some comparisons between the evolution of computer languages and natural languages. One such is Turing completeness: native speakers express everything they want with a limited toolkit of vocabulary, sounds, and syntactic rules that must not be too hard to master. Another parallel is the slow, incremental nature of language evolution. Languages avoid breaking changes, given the billions of lines of legacy that would otherwise be rendered unreadable.
What does this mean for Java? It makes sense to stay true to its OO roots and optimize its expressive potential, rather than introduce paradigm shifts that break backward compatibility. To remain in the same language, big syntactic overhauls are unlikely. Mutability and void statements will stay part of the language, despite the growing appetite for more functional languages features. This is fine because the JVM platform can offer the best of two worlds. Interoperability of byte code allows radically different languages (Frege, Clojure) easy access to a rich and stable ecosystem. Java doesn’t have to be the golden hammer of programming.
Expressions Instead of Statements
Meanwhile, Java is not oblivious to what’s happening in competing languages on the JVM like Kotlin and Scala. Reactive and asynchronous programming models (think Akka and Coroutines) favor a functional style of coding. The traditional, imperative style to manipulate data through explicit instructions is synonymous with state change and leads to intractable side effects when done badly. Void methods that mutate the state of the instance they’re defined in (or worse, its arguments) are treated as the code smells that they are.
Java has caught up by allowing switch statements as expressions. The preference in favor of statements is clear. We may even see if-then-else blocks receiving a similar transformation. Immutability in the shape of records is another movement, great for parallel processing since it eliminates much expensive synchronization.
Contenders on the JVM can outdo Java with more powerful changes that are hard to introduce if you remain committed to backward compatibility. So, many minor irritants won’t be solved, and some beloved features can’t be introduced. The List interface will not turn immutable by default. Checked exceptions are here to stay. Java will not get Kotlin’s nullable type system. No question mark about it.
Haskell and Lisp on the JVM
How about a fork towards a strict superset, like Typescript did with JavaScript, introducing a more expressive language? Regardless of the challenges, there is no taste to design such a Java++ superset. We don’t need it because we already have several mature JVM languages that cater to radically different paradigms. You can even do Haskell and Lisp on the JVM with Frege and Clojure, respectively. The future will see more polyglot projects. In an architecture with multiple teams working on loosely coupled services, it’s plausible to develop some services in Scala, Kotlin, or Clojure if that is more appropriate.
Language popularity contests based solely on search engine stats are crude measurements, but it’s fair to say that the JVM competitors have yet to make a serious dent (say, more than 10% reduction) in the number of projects that still choose Java as their main language. The same JVM platform that welcomes these alternatives also remains the powerhouse that keeps Java in the saddle. Faster release cycles mean you can enjoy new features more quickly. The interval for long-time support versions may be shortened to two years. All this makes an enterprise-wide transition to Kotlin or Scala less attractive.
Critical Mass
Alternative JVM languages suffer from a smaller pool of expertise – nice for developers, less so for their employers. Their ecosystem of dedicated tooling is often smaller and not as mature. Sure, many of these drawbacks are growing pains. Time can heal some, and the competition may eventually take a sizable bite out of Java’s popularity. If one contender gathers enough momentum, Java might be done for in the long term – I’d put my money on Kotlin though, not Scala. Even so, it would only make the JVM as a platform more appealing.
Lastly, we shouldn’t get too hung up on the superior expressivity of this or that language. Just because you can develop something as a Java enterprise project doesn’t mean you should. The non-functional requirements for such projects (security, scalability) have made it almost impossible to be a full stack developer who is equally well-versed in Java backend, the JavaScript web framework du jour, UX, and cloud deployment.
The Biggest Golden Hammer of All
Coding itself may be the biggest golden hammer of all. We are tinkering less with source code and more with configuration. Low-code and no-code platforms offer a deliberately constrained programming platform for generic business problems, as an antidote to this overload of complexity. Don’t treat them as a silver bullet or as a golden hammer, mind you.
They are suited to vanilla business problems. And if you can’t click and drag your way out of some non-trivial business logic, you can usually insert some snippets of custom code to deal with it. Written in Java, for example.