TL;DR – Technically no, but practically yes.
I’ll start by examining why Java is not a monoculture.
Java is interesting, in that it is both a programming language and a runtime environment. When Java first burst upon the scene, it was promoted as a general-purpose cross-platform programming language. It was not the first language to make such a claim, but it was the first to live up to most of its hype. I was a very early adopter of Java and remember the joy of writing code on a Windows PC and then having my tester run that exact same code on their Apple Mac. I’d used scripting languages that could do that, but never anything that needed compiling. I was hooked.
Java achieves this cross-platform behavior by using a runtime environment that is compiled for each target environment. This is known as the Java Virtual Machine, or JVM to its friends. Any operating system that has a JVM can run Java code unchanged. Java wasn’t the first language to use this approach. Other languages were intended to be able to be easily ported. C is perhaps the canonical example of this, being intended to allow the Unix operating system to be ported to other different architectures. The Pascal language compiled to p-code which was then run by the Pascal runner. But, for all of this, there had never been any emphasis on taking this to the next level and making languages runnable cross-platform.
Making Java cross-platform took a special form of dedication by the good folks at Sun Microsystems. It didn’t hurt that they didn’t realize they were writing a general-purpose programming language. At the time the language was being developed, it was intended to be targeted at embedded software development. The target for Java (back then called “Oak”) programs was consumer electronics, anything from toasters to set top boxes for controlling TVs. Consumer electronics are massively price sensitive and it is not unusual for the target CPU in a device to be changed during the project if another CPU becomes available cheaper. Sun Microsystems addressed this by designing the JVM to hide the physical CPU and all hardware access under the covers. Effectively, the JVM becomes a virtual CPU on top of the physical CPU, with its machine code instructions commonly called bytecode. This way the programmers did not need to worry about the physical hardware in the device and could program it against the unchanging environment of the JVM. Similarly the hardware team could utilize anything they wished, or that was cheap, without fear of upsetting the programmers as long as the JVM could be made to run on it. And given that the JVM was written in pure C, just like the highly portable Unix operating system and used all of the portability experience that came from porting Unix, it turned out to be fairly straight-forward to get the JVM running on almost any platform.
In the end, Java was not used for consumer electronics, but was instead released as a general-purpose programming language. Its heritage of extreme portability shone through and Java became the first successfully cross-platform compiled programming language.
A consequence of the emphasis that Java was both a programming language and a runtime environment had an interesting effect. Programming language designers have targeted the JVM for the compiled executables from their languages. To this end, there are over a hundred different languages that are either written specifically for the JVM or that have the JVM as one of their available compilation targets. A number of these languages have become quite successful in their own right. Groovy, Scala and Clojure come to mind easily. These languages are very different from Java, but all run natively, by design, on the JVM. Groovy looks like someone merged a Ruby interpreter into the Java compiler, Scala is an object-functional language and Clojure is a powerful and highly functional Lisp that is now popular with some of the biggest names in the Java ecosystem.
Within the Java runtime system, code written in different languages may be used together. As long as a portion of code targeted for the JVM follows all of the standard calling and naming conventions, any other bytecode can call it as simply as any pure Java code. You can have a Scala program calling Groovy utilities that use Java objects and a Clojure library. And with the Java Native Interface (JNI) you can even call compiled native code on the platform you’re running on if you have a very specific need.
With all of this interoperability, it seems clear to me that the Java ecosystem is vibrant and powerful. There are many different languages available that can work and coexist in harmony. With this perspective, it is clear that Java (speaking of both the language and ecosystem as a whole) is not a monoculture.
And yet, within large corporations, Java feels exactly like a monoculture. Let’s look at why this is.
Java’s place and role in large corporations today is very different from how it was nearly 19 years ago when I first started using it. Java was a brand new programming language. It was untested as a business language. But it did have some intriguing promises of platform portability and a certain amount of initial respect afforded it because it came from Sun Microsystems. The early adopters all tried it out and enough of them decided it was worth continuing with, that they started sneaking it into their workplaces.
Java had not yet come to the attention of managers, so asking to use it was unlikely to work. Programmers instead, did what programmers have always done, and they started using it anyway. I started using Java at a previous employer without any managers knowing. My supervisor knew, but no managers. And my project was a success, so now Java is an approved technology there. While it seems hard to imagine, Java was not the business programming language of choice for several years after its release. Even after a year or two, it was still necessary to present arguments as to why your project should use Java. This is completely opposite to how Java is perceived by businesses now. Unless you work at an all Microsoft shop, a university or a startup, there’s a really good chance that you’ll not only be working in Java, but that you were mandated to work in Java.
Once businesses embraced Java, they immediately started trying to quash all creativity out of their Java programmers. Not out of any malicious ill will, but because they felt that they needed to standardize their use of it. Specific versions of Java were mandated, coding standards were often written and enforced, specific IDEs were required to be used. Some companies even prohibited any use of free or open source software libraries or utilities. Everything had to be standard and approved.
This condition has not changed much since those early days. Companies still mandate the version of Java that may be used, the brand and version of application server and every library that you may wish to use in your application still needs to be approved first. These standards do get updated, but generally at glacial speeds. I recently took a two year sabbatical from programming to work as a full-time pastor. Before that sabbatical, most companies were using Java 6, upon return I found that they were still using Java 6 despite Java 9 being in the public stages of being released.
And this is why I believe that Java is a de facto monoculture. Corporations are so determined to standardize everything (for the sensible and noble purpose of reducing risk) that they slow themselves down and reduce their ability to take advantage of new technical advancements. These missed opportunities span a range from new versions of programming languages they’re already using to new languages, techniques or paradigms. Should a large corporation be using these new languages and paradigms? Maybe or maybe not, but the problem is not whether they should use them, but that because of their own risk-averse behavior, they can’t use them!