Introduction to JVM Languages

By Vincent van der Leun
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Java Virtual Machine

About this book

Anyone who knows software development knows about the Java Virtual Machine. The Java Virtual Machine is responsible for interpreting Java byte code and translating it into actions. In the beginning, Java was the only programming language used for the JVM. But increasing complexity of the language and the remarkable performance of the JVM created an opening for a new generation of programming languages. If you want to build a strong foundation with the Java Virtual Machine and get started with popular modern programming languages, then this book is for you.

The book will begin with a general introduction of the JVM and its features, which are common to the JVM languages, helping you get abreast with its concepts. It will then dive into explaining languages such as Java, Scala, Clojure, Kotlin, and Groovy and will show how to work with each language, their features, use cases, and pros and cons. By writing example projects in those languages and focusing on each language’s strong points, it will help you find the programming language that is most appropriate for your particular needs.

By the end of the book, you will have written multiple programs that run on the Java Virtual Machine and know about the differences between the various languages.

Publication date:
June 2017
Publisher
Packt
Pages
450
ISBN
9781787127944

 

Chapter 1. Java Virtual Machine

Java Virtual Machine (JVM) is a modern platform on which you can develop and deploy software. As the name implies, it was originally created to power applications written in the Java language. However, it didn't take language designers long to realize that they could not only run their languages on JVM, but also take advantage of its features and extensive class library.

Sun Microsystems released Java and the first JVM implementation in 1995. With its focus on Internet applications, Java quickly became popular. It was also designed from the ground up to run anywhere. Its initial goal was to run on set-top boxes, but when Sun Microsystems found out the market was not ready at that time yet, they decided to bring the platform to desktop computers as well. To make all those use cases possible, Sun invented their own binary executable format and called it Java bytecode. To run programs compiled to Java bytecode, a JVM implementation must be installed on the system.

This book will help you get started with five most popular languages that target JVM. By learning the language fundamentals and writing code yourself, you will be able to find the language that best suits you, your team, and your projects.

Before we dive into the Java Development Kit (JDK) and Java Class Library in the next chapter, we will look at some practical points first. With so many competing programming languages and platforms available today, it makes sense to first take a detailed look at what JVM has to offer to developers. Therefore, we will cover the following topics:

  • Reasons for developing on JVM
  • Popular use cases of JVM
  • Introducing JVM concepts
  • Java editions
  • Other languages on JVM
 

JVM implementations


It's important to note that this book focuses on JVM implementations compatible with Oracle's Java SE (Standard Edition) 8 (and higher) platform only. This version can be installed on desktop computers, servers, and many single-board computers (including all the models of the popular credit-card-sized Raspberry Pi). We will use Oracle's implementation in this book, but both the open source OpenJDK and IBM's own J9 Java SE implementations of the same version should work equally well.

The Java platform as published by Google on Android phones and tablets is not covered in this book at all. One of the reasons is that the Java version used on Android is based on an older version of Java. While progress has been made to make Android's version of the platform more up to date, it still doesn't have all the features of Oracle's Java SE 8, and it requires different compilers and tools. Another reason is that Google omitted a lot of the Java SE APIs and replaced them with their own unique, incompatible APIs. Some of the languages covered in this book can be used with Android, however. Kotlin, in particular, is a very popular choice for modern Android development. This use case will not be explored in this book, though.

 

Why develop on JVM?


With so many programming languages and platform options available today, why would you consider developing and deploying your next project on JVM? After all, Java, the language that JVM was originally built for, has been declared obsolete (and, ridiculously, even dead) by fans of different languages more times over the years than anyone cares to remember.

Yet, while many other programming languages have come in and gone out of the spotlight, Java has always managed to return to impressive spots, either near or lately even on top of the list of the most used languages in the world.

Let's look at some of the most important reasons why the JVM platform is so strong:

  • It keeps up with the modern times by adapting to market changes
  • The Java Class Library, the built-in library of classes, is very strong
  • It has an unmatched ecosystem

JVM adapts to market changes

When Java first appeared in the mid-1990s, computers had CPUs with only a single core and didn't have gigabytes of memory, as memory chips used to be prohibitively expensive. Java is one of those languages that kept up with modern developments: when multicore CPUs appeared, Java was soon able to support those additional cores when running code in multiple threads. But it did not stop there. In each newer version, it added new classes that made working with concurrency easier. This trend still continues.

When the functional programming paradigm became popular, Java received built-in support for lambdas and streams in the core language. While Java was quite late to get this support, compared to other popular languages, Java's implementation was better than many others. This was because it offered built-in support for multithreading almost for free.

Adapting to market changes also means that sometimes things have to go. Back when Java was introduced, running Java code directly in the browser was a big thing. These mini applications were called applets and required a custom browser plugin for each browser and system. Of course, we now know that the market has chosen the JavaScript language as the standard language to create interactive websites, and Oracle recently deprecated the applet standard.

Java Class Library

For each edition of Java (more on available editions later in this chapter), it has been decided which classes are guaranteed to be available in a JVM implementation of a specific version. The Java Class Library for Java SE 8 is a very large collection of classes, and every JVM runtime installation that adheres to the Java SE 8 platform standard must implement those classes, regardless of the vendor of the JVM implementation.

Classes in this library provide functionalities such as writing or reading from the console window, performing file I/O, and communicating with TCP servers. Also, there are many classes available to start and manage operating system threads. More fundamentally, it contains classes that define data structures, such as lists and maps (called dictionaries in some other languages), among many others. In the next chapter, we will thoroughly look at the classes in the Java Class Library.

The Java Class Library is an important reason why language designers love targeting JVM. Using the data structures defined in the Java Class Library, they are able to focus more on the language design and less on building a full runtime library from scratch. Building a fully tested, multiplatform runtime system library comparable to the Java Class Library is a huge undertaking.

Ecosystem

A built-in class library can obviously not cover all the use cases of a programmer. If something is missing, you can turn to libraries and tools built by other companies, groups, and individuals to save time. Because Java has been so successful for many years, its ecosystem is unmatched. It will be hard to find a platform with proven high-quality tools, libraries, toolkits, and framework choices that are better than the ones available in JVM.

With so many add-on libraries available, Java hardly ever pushes the developer in a certain direction. As an example of how rich the ecosystem is, let's look at the main options JVM developers typically have when creating a web application:

  • Build a web application that runs inside a JVM application server
  • To quickly have results, a general high-level web framework can be used
  • For more control, the application can be built with a microservice framework

Scenario 1 —“ Using a JVM application server

Developers could take the enterprise route and install a JVM-based application server, either a free open source one or a paid proprietary one, that will run the application along with web applications simultaneously, if desired. The server will handle configuration issues and manage database connections.

There are simple application servers available that just contain enough built-in APIs to run basic web applications. But there are also full blown Oracle-certified application servers that have a magnitude of built-in and standardized APIs, including APIs to access databases, generate or consume XML or JSON documents, communicate with other web services via the SOAP or REST standards, provide web security, send or receive messages from legacy computer systems, and many others.

The two most important frameworks for enterprise development are the following:

  • Oracle's Java Enterprise Edition (Java EE) platform, covered later in this book
  • The Spring Framework ecosystem (including Spring Boot)

Many applications use both these technologies together.

Some of the popular application servers are the following:

  • Apache Tomcat (for basic web applications)
  • Apache TomEE
  • Red Hat WildFly
  • Oracle GlassFish
  • Red Hat JBoss Enterprise Application Platform
  • Oracle WebLogic

The first four are open source and the last two proprietary.

Scenario 2 — Using a general high-level web application framework

The second possibility would be to use a complete web application framework. These frameworks usually offer higher-level APIs than enterprise frameworks and offer built-in model-view-controller (MVC) solutions that have the capability to enhance a developer's productivity significantly.

Frameworks such as these usually dictate or steer the developer in a certain direction as they have built-in support for only a few hardcoded libraries/toolkits. Often, plugins are supported to add support to other choices, however. By giving up some freedom, quick development cycles can be achieved. Some frameworks require that the application is run inside a JVM application server, while other frameworks provide their own HTTP server.

Apache Struts used to be very popular in this category, but nowadays, the Play framework is probably the most popular choice.

Scenario 3 —€“ Using a microservice framework

A different choice could be to create the application using the modern microservice framework. Frameworks such as these have a built-in HTTP server to run your application, but they do not provide any other tools or libraries out of the box. In this scenario, it's easier to mix and match other libraries and toolkits that you want to use yourself.

Commonly, the application will be separated into multiple standalone web services to follow the modern microservice architecture, but this is not a strict requirement of these frameworks.

Vert.x and Spark Java (not to be used with the Apache Spark big data platform) are the most commonly used microservice frameworks.

 

Popular use cases


Now that we've seen some valid points that confirm why JVM is a viable platform for modern software development, let's look at some places where JVM usage is particularly popular:

  • Web applications
  • Big data analysis
  • Internet of Things (IoT)

Web applications

With its focus on performance, JVM is a very popular choice for web applications. When built correctly, applications can scale really well, if needed across many different servers.

JVM is a well-understood platform, meaning that it is predictable. Plus, it provides many tools to debug and profile problematic applications. Because of its open nature, monitoring of JVM internals is also possible. For web applications that have to serve thousands of users concurrently, this is an important advantage.

JVM already plays a huge role in the cloud. Popular examples of companies that use JVM for core parts of their cloud-based services include Twitter (famously using Scala), Amazon, Spotify, and Netflix. The actual list is much larger.

Big data

Big data is a hot topic. When data is regarded too big for traditional databases to be analyzed, one can set up multiple clusters of servers for processing such data. Analyzing data in this context can, for example, refer to searching for something specific, looking for patterns, and calculating statistics.

This data could be obtained from the data collected from web servers (for example, logged visitors' clicks), the output obtained from external sensors at a manufacturer's plant, legacy servers that have been producing log files for many years, and so forth. Data sizes can vary wildly as well, but often, they take up multiple terabytes in total.

Two popular technologies in the big data arena are the following:

  • Apache Hadoop (provides storage of data and takes care of data distribution to other servers)
  • Apache Spark (uses Hadoop to stream data and makes it possible to analyze incoming data)

Both Hadoop and Spark are for the most part written in Java. While both offer interfaces to a lot of programming languages and platforms, it will not be a surprise that JVM is one among them.

The functional programming paradigm focuses on creating code that would run safely on multiple CPU cores, so languages that are fully specialized in this style, such as Scala or Clojure, are appropriate candidates to be used with either Spark or Hadoop.

IoT

Portable devices that feature Internet connectivity are very common these days. Since Java was created with the idea of running on embedded devices from the beginning, JVM is, yet again, at an advantage here.

For memory-constrained systems, Oracle offers the Java ME Embedded platform. It is meant for commercial IoT devices that do not require a standard graphical or console-based user interface.

For devices that can spare more memory, the Java SE Embedded edition is available. The Java SE Embedded version is very close to the Java SE discussed in this book. When running a full Linux environment, it can be used to provide desktop GUIs for full user interaction.

Both Java ME Embedded and Java SE Embedded platforms can access the general-purpose input/output (GPIO) pins on the Raspberry Pi, which means that sensors and other peripherals connected to these ports can be accessed by Java code.

 

JVM concepts


Every aspiring JVM developer should be familiar with its most important concepts:

  • JVM is a virtual machine
  • Most implementations feature a just-in-time (JIT) compiler
  • It offers a few built-in primitive datatypes
  • Everything else is an object
  • Objects are accessed via reference types
  • The garbage collector (GC) process removes obsolete objects from memory
  • Build tools are used a lot in the JVM world

Virtual machine

That the Java Virtual Machine is a virtual machine is a rather obvious observation, but it should be kept in mind. One of the consequences is that you are, in theory, writing applications for a type of machine that differs from the machine you are developing or running your applications on.

It generally does not matter whether the code runs on a 32-bit or 64-bit version of the Java Runtime Environment (JRE). The latter will probably make more memory available to the application than the 32-bit version, but the running program will not care about this difference as long as it doesn't make native operating system calls or require gigabytes of memory.

Note

Unlike a language, such as C, where datatype sizes are dependent on the native system, Java does not have this issue (or feature, depending on your point of view). An int integer on JVM is always signed and is of 32-bit size, no matter on which computer platform or system architecture it is running.

Finally, it should be noted that each application that runs on JVM loads its own instance of JVM on system memory. This means when you run multiple Java applications at the same time, they will all have their own copy of JVM at their disposal; this also means different applications can use different versions of JVM if required for whatever reason. For security reasons, it is not suggested that you have different versions of the JDK or JRE on one system; it's usually better to have only the latest supported versions installed.

The JIT compiler

Although not dictated anywhere, all popular JVM implementations are not just simple interpreters; they feature complex JIT compilers along with their interpreters.

When you launch a Java application, JVM is launched and initialized first. Once this is done, it immediately starts interpreting and running the Java bytecode. If the interpreter believes it makes sense, it will compile sections of the programs and load libraries to native executable code in memory and start executing that version of the code instead of the interpreted Java bytecode version. This often results in code that could be executed much faster.

Whether the code is compiled or interpreted depends on many things. If a routine is called often, it becomes a probable candidate for the JIT compiler to compile it to the native code.

Note

The advantage of the JIT approach is that the distributed files can be cross-platform and the user does not have to wait for native compiling of the whole application. Applications start executing immediately after JVM is initialized, and the optimization is done under the hood.

Primitive datatypes

JVM has a few so-called built-in primitive datatypes. This is the main reason why Java is not considered a pure OOP language. Variables of these types are not objects and always have a value:

Java name

Description and size

Values (inclusive)

byte

Signed byte (8 bits)

-128 to 127

short

Signed short integer (16 bits)

-32768 to 32767

int

Signed integer (32 bits)

-231 to 231-1

long

Signed long integer (64 bits)

-263 to 263-1

float

Single-precision floating point (32-bit)

Non-precise floating point values

double

Double-precision floating point (64-bit)

Non-precise floating point values

char

A single Unicode UTF-16 character (16-bit)

Unicode character 0 to 655535

boolean

Boolean

True/False

 

Note that not all JVM languages support the creation of variables of primitive types and follow this modern assumption: everything takes the object approach. We will see that this is usually not a problem as the Java Class Library has wrapper objects that wrap primitive types, and most languages, including Java, automatically use these wrappers when required. This process is called auto-boxing.

Classes

Functions and variables are always declared inside a class. Even the application entry function that is called upon a program launch, called the main() function, is a function that is located inside a class.

JVM only supports the single-inheritance model. Classes always inherit from one class at the maximum. This is not a big loss. As we will see in the next chapter, a structure called an interface comes to the rescue. An interface is basically a list of function prototypes (only the definition of functions, without code) and constants. Classes that implement an interface are required by the compiler to have implementations for those functions. Classes can implement as many interfaces as they want, but they must provide implementations for each method of all the implemented interfaces.

Note

Some languages covered in this book hide these facts completely from the developer. For example, unlike Java, some languages allow functions and variables to be written outside class declarations or even executable code outside function definitions. Other languages support inheritance of multiple classes. Internally, these languages do clever tricks to work around JVM limitations and design decisions.

JVM classes are usually grouped in packages. In the next chapter, we will see how classes are organized.

Reference types

Like most modern programming languages, JVM does not work with direct memory pointers to objects; it uses reference types. A reference type variable either points to a specific instance of a class or it points to nothing.

If a reference type points to an object, it can be used to call the object's methods or access public attributes.

If a reference type points to nothing, it is called a null reference. When calling methods or reading attributes using a null reference, an error will be generated at runtime. We will see that some languages covered in this book came up with solutions to this common problem.

References and null references

Let's take a look at the following code:

    Product p = new Product();
    p.setName("Box of biscuits");

Assume that Product is a class here that is available to the program. We create a Product instance and the p variable points to it. We then call the setName method on this object instance.

JVM does not give direct access to the memory location where the Product object is stored. It just provides a reference to the created object. When using the variable p, JVM figures out which memory location it has to reach for the object that the variable points to.

We add the following lines to the previous snippet:

    p = null;
    p.setName("This line will produce an error at run-time");

A reference can be cleared explicitly by assigning null to it. Note that this is not necessary for variables declared inside a method, as they will be cleared automatically upon exiting the method. However, it is perfectly acceptable to still do it anyway. Now the variable p is a null reference. In the next paragraph, we will see what will happen to object instances that are no longer referenced by any reference type variable.

The preceding code will compile fine. When running the program, the last line will cause a NullPointerException error, though. If no error handling capability was implemented in the application, it will crash. Many modern IDEs try to detect these situations and warn the developer about them.

Garbage collector

JVM does not require the programmer to manually allocate and release blocks of memory when creating or disposing of objects. The programmer can generally concentrate on just creating objects when he or she needs them.

A process known as the GC halts the application at certain intervals and scans the memory for objects that are no longer in scope (not reachable by any other object loaded at that point). It will remove those objects that can safely be deleted from the memory and reclaim the freed space.

This process used to cause very serious performance issues in the past, but the algorithm has improved much over the years. Also, if an application needs it, system administrators can configure many parameters of the GC to better control it.

The developer should always keep the high-level concept of the GC algorithm in mind. If you keep creating tons of objects and always keep them in the scope (meaning making it in such a way that all those objects can be reached, for example, by storing them in a list that the application can access), then out of the memory, errors are very likely to occur sooner or later.

Example

Let's assume you have developed an e-commerce application for an online store. Also, let's assume that each logged-in user has their own ShoppingBasket instance that holds the products that they add to their basket.

Say, a user has logged in today and is planning to buy a soap bar and a delicious pack of cookies. For this user, the application will create two Product instances, one for each chosen product, and add them to the products list of ShoppingBasket:

Just before visiting the checkout page, the user sees that Amazon offers the same cookies at a much better price and decides to remove the cookies from the basket. Technically, the application would remove the Product instance from the list of products. But from there on, the product instance representing Chocolate cookies is an orphan object. As there is no reference to it, it cannot be reached by the application:

After a while, JVM's GC kicks in and sees the Chocolate cookies object instance. It determines that the object cannot be reached in any way by the application anymore and therefore decides to remove it. The memory the object was using up will now be released:

There are several tricks to tame GC. One well-known trick when an application needs to work with lots of similar objects is to put these objects in a pool (list of objects). When an application needs an object, it simply gets one from the pool and modifies the object according to its needs. When it has finished and doesn't need the object anymore, it will put it back in the pool. Since these objects are always in the scope (when not used, in the pool, which the application can access), GC will not try to dispose of these objects.

Backward compatibility

The maintainers of JVM and Java Class Library understand the needs of business developers. The code that is written today should ideally run tomorrow. JVM offers reasonable backward compatibility. Developers familiar with Python 2 and 3 will know that this is not a given in the industry.

Newer JVM versions can run applications that were compiled for older JVM versions, as long as the application's code does not use APIs or technologies that were removed from the JVM version that is running the application. Here's an example: libraries compiled for Java 6 can still be loaded and used in projects that run on a Java 8 JVM instance. But this is not the case the other way around; applications running on a Java 6 JVM instance cannot load classes compiled for later versions.

Of course, like every other platform or language, the JDK and Java Class Library maintainers have to deprecate classes and whole technologies from time to time. While there are issues, backward compatibility on JVM is generally better than many other platforms and languages. Also, APIs are generally only removed if proper and well-documented alternatives exist.

Build tools

Back when projects were simpler, simple batch or operating system shell script files used to automate the compiling and packaging process. As projects became more complex, it became harder to define these scripts. For different operating systems, completely different scripts had to be written.

Soon, the first set of dedicated Java build tools appeared. These worked with XML build files. More or less, cross-platform compatible scripts could be written this way. At first, long and cumbersome scripts had to be written; later, tools worked with the convention over configuration paradigm. When using the conventions suggested by the tool, much less code has to be written; however, if your situation is different from the default behavior, it can take a lot of effort to let the tools do what you want or need. Newer tools ditch XML files and provide script languages to automate the building.

Some of the features that many of those tools offer are as follows:

  • Built-in dependency managers that can download add-on libraries from well-known repositories from the Internet
  • Automatically run unit tests and conditionally stop packaging if a test fails

The JDK does not offer a build tool itself, but it will be hard to find projects that do not at least use one of the following open source build automation tools:

  • Apache Ant (has no built-in dependency manager and works with XML-based build scripts)
  • Apache Maven (introduced convention over configuration with XML files and works with plugins)
  • Gradle (build scripts written in Groovy or Kotlin)

JVM programmers that use a popular IDE do not have to worry too much about build automation tools. This is because all IDEs can generate build scripts themselves. If you want more control, you can start writing your own scripts manually and let the IDE use that script to compile, test, and run your project.

 

Java editions


Several editions of Java are available. Each one aims at different use cases. Some of the editions have had numerous name changes over the years; the current names of the editions are as follows:

  • Java Standard Edition (Java SE)
  • Java Enterprise Edition (Java EE)
  • Java Micro Edition (Java ME)

Java SE

This is the most important edition. When people mention the term "Java," they usually refer to this edition. This book concentrates solely on the Java SE platform.

This edition is meant to run on desktop machines and servers, and as we will see later, an embedded version is also available and bundled with Raspberry Pi's Linux distribution. Java SE comes with the complete Java Class Library. It includes the classic Swing GUI Toolkit; most versions also contain the modern JavaFX GUI toolkit.

Note

Note that a recent update of Java SE Embedded removed the JavaFX toolkit. Once you install this JDK update on the Raspberry Pi, the JavaFX component will be gone. Oracle has open sourced their JavaFX port for the Raspberry Pi so that advanced users can still download and compile it.

Java SE is mostly meant to create standalone consoles, desktop GUIs, or headless applications; alternatively, it is used to create external libraries.

Java EE

Java EE builds upon Java SE; therefore, it requires that Java SE is installed. It adds lots of APIs in a lot of categories. Java EE applications usually run inside JVM application servers. This book does not cover Java EE in depth but will mention it from time to time. This is because it is a very important addition to the Java platform, especially for business developers.

It is not possible to download a Java EE standalone edition from the Oracle website; instead, you will have to download a full application server that is compatible with the Java EE platform version you want to use. Some IDEs bundle the Java EE application server as well; we will cover this in the next chapter.

The Java EE standard only describes the APIs that must be available, but it does not dictate the implementation. It's up to the Java EE-compatible application servers to come up with actual implementations that adhere to these standards.

Example —€“ Java Persistence API as implemented by two application servers

Java EE describes the Java Persistence API (JPA). It is an object relation mapper (ORM) API, a layer between Java objects and relational databases (often SQL databases, such as Oracle database, Oracle MySQL, PostgreSQL, and so on). With a few lines of code, the content of JVM objects can be written to the database or vice versa: read from the database and put in the object.

Oracle's own reference implementation of Java EE is an open source application server called GlassFish. GlassFish bundles the existing EclipseLink open source project as the implementer of the JPA standard. Meanwhile, Red Hat's WildFly, a different open source Java EE application server, bundles Red Hat's own, more popular, Hibernate ORM open source project, which also implements the JPA standard.

If developers only use the features documented in the JPA standard, then it should not matter to them which implementation is used, but problems arise once features are used that are unique to a specific implementation.

Note

If you do not agree with the choices made by the vendor of your application server, it is often possible to switch implementations of a particular standard. Yes, JVM developers really love having choices!

Java ME

Before the days of iOS and Android, Java ME happened to be an important platform for feature phones and early smartphones for games and some basic applications. iOS and Android both never supported Java ME applications, so nowadays it does not play a major role anymore.

It featured a subset of the Java Class Library and offered some additional APIs to work with mobile devices. Java ME got a second life as Java ME Embedded, which can be used for commercial IoT devices.

 

Other languages on JVM


To promote the Java language and platform, Sun published JVM specifications early on. This document was meant for developers who wanted to write a JVM implementation themselves, perhaps for platforms that did not have an official JVM implementation available yet. It described which low-level commands JVM can execute, the required data structures, rules on accessing memory, the Java bytecode's .class file format, and much more.

While not originally a goal of the designers, the release of the specifications also made it possible for other language writers to experiment with the Java bytecode, and it didn't take long before other languages could compile to that format. Sun, and later Oracle, liked this development a lot. They liked it so much that Oracle even added new features to JVM, solely to make it easier to support dynamic languages on JVM.

In this chapter, we will cover the following topics related to alternative JVM languages:

  • Understanding why should we choose a language other than Java for JVM development
  • Discussing the possibility of mixing languages in a single project and the issues that would be expected
  • Writing unit tests in a different language than the one used in the main project

Why choose a language other than Java?

Since Java is a language that is originally designed to run on JVM, why would anyone choose another language for JVM development?

There are several reasons why a developer would do this:

  • Java is a very verbose language
  • Not everyone likes statically typed languages and they are not always the best solution
  • Java Class Library misses some classes for common use cases

Java is a very verbose language

Java is notorious for being very verbose. While over the years there have been updates made to the language to improve the situation a bit, many other languages require the writing of less code for the same end result.

Let'€™s look at a simple example.

A standard mutable object often looks like this in Java:

    class Person {
      private String name;
      public Person(String name) {
        this.name = name;
      }
      public String getName() {
        return name;
      }
      public void setName(String name) {
        this.name = name;
      }
    }

In Kotlin, the following line of code does the same (and more):

    data class Person(val name: String)

No, this is not a joke. Kotlin automatically implements the same methods as shown in the Java example when compiling this code. In fact, it adds even more commonly used methods than shown here in the Java example. In Chapter 4, Java Programming, we will discuss those additional methods for Java as well.

Note

While productivity can be seriously enhanced by choosing a different language, the situation for Java is not as bad as it may seem. All modern IDE programming tools can automatically generate boilerplate Java code, such as the one shown in the preceding code, with a simple key press combination.

Java is not ideal for everything or everyone

While Java recently received some serious functional-programming-like features in version 8, at its core, it is still a statically typed imperative language. Not all developers prefer this programming style. Programmers coming from Python or Ruby may cringe when they have to write code for a fully typed language. This can be a valid reason for teams to adopt a different language than Java for JVM development.

Also, some problems can be solved much more elegantly when using a dynamic programming language, while for projects that require complex concurrency situations, a functional programming style is often more suitable. Finally, some libraries and frameworks simply feel more natural when used with certain languages.

Missing classes in Java Class Library

Java Class Library is an extensive library, but sometimes, it simply misses certain classes or it shows that it was introduced more than two decades ago. While missing functionality, in most cases, can be solved by adding free and open source add-on libraries from the JVM ecosystem, it can both be convenient and a time-saving option if you use a language that has built-in solutions for these problems.

A good example of missing functionality in Java SE version 8's Java Class Library is that it currently has no built-in support for the very commonly used JSON standard. Popular add-on libraries that provide JSON support include Jackson and Google's GSON. Also, modern versions of the Java EE platform provide APIs for JSON support classes. Some languages covered in this book have built-in support for JSON.

Another issue is that some popular classes from Java Class Library require a lot of boilerplate code to use them effectively. Languages, such as Groovy, add wrappers to many popular Java Class Library classes that make these APIs much easier to use.

Mixing JVM languages in a project

Many languages offer good interoperability with Java, and therefore, with other JVM languages as well. Languages do this if they use standard Java Class Library classes, where possible, for their data structures and compile methods in a similar way as Java would have done.

It's not uncommon to have certain classes compiled in a different language in a Java project. One should be aware of the various issues, though:

  • It can complicate the building process a lot
  • Many languages require their own runtime classes that can cause issues

Increasing build process complexity

When combining multiple languages, the build script has to be adapted, and this can lead to complex situations. For example, if a Java project uses a class compiled in Groovy, the order of the compilation is important. First, the Groovy class will have to be compiled, then the Java code. If that Groovy code uses custom classes from the Java project, then it gets even more complex.

Note

Groovy is a special case, as we will see in the Groovy chapter. The Groovy compiler can compile most Java code, as the Groovy language is largely compatible with the Java language. For projects where this is not possible, or desirable, there's a compiler plugin for the Apache Maven build tool that solves many of these problems.

A solution could be to divide the code into multiple subprojects and list the resulting libraries as a requirement of the main project in the build tool.

Some languages offer another solution: they provide their own custom classes to call the language's source code from Java (or any other JVM language). The source code is then dynamically compiled to the Java bytecode on the fly as these classes load the code. Other languages implement an official standard to embed script languages in Java code. We will take a brief look at this in the appendix when we discuss Oracle's JavaScript interpreter, called Nashorn.

Language runtime libraries

This is somewhat related to building complications. Many alternative JVM languages require their own support libraries to be bundled with the compiled program. These libraries often define data structures that are unique to the language and internal support methods that the language's compiled Java bytecode calls.

This is usually not a problem, but things can get problematic if one of the project's dependencies (or one of the project's dependencies' dependencies...) is compiled in the same language but of a different version. Things can get messy when a different version of the same runtime library is required by multiple libraries of the same project and can result in confusing error messages when compiling or running the project.

This situation is called dependency hell and is not really specific to the usage of multiple languages in a single project, but it is something every developer should be aware of. Developers that want to mix languages should also be aware of the fact that sometimes language runtime libraries increase the final size significantly and that some runtime libraries also bundle their own dependencies; this increases the risk of dependency-hell-like problems. Often, dependencies are documented in the language's documentation or website.

Note

Many language developers, like many framework designers, are aware of these problems and have taken steps to minimize the risks of these problems. For example, they commonly fork the more popular dependencies they use themselves and rename them to prevent class name clashes.

Writing unit tests in a different language

It is quite a common approach to test Java code with unit tests written in a different language. As we have seen earlier in this chapter, the code in other languages can be much more compact than the same code in Java, which is ideal for writing small, concrete, and readable unit tests.

Since the language's runtime library will only be used while running the unit tests, the language's runtime library itself will only be used while executing the tests and it will not have to be bundled with the compiled main project.

Note

Groovy is especially suited for this use case, as we will see in the Chapter 11, Groovy. It has some convenient features for writing unit tests, including a built-in assert statement that prints very verbose and readable output when the passed value is different than the expected output.

 

Summary


In this chapter, we described JVM from a fairly higher-level. We started by looking at what JVM offers to developers and also focused on popular use cases and the most important JVM concepts. We also examined the available Java editions. Finally, we covered alternative JVM languages by looking at several possible reasons why a developer would choose another language than Java for JVM development.

In the next chapter, we will install the JDK and take a detailed look at it. We will also cover Java Class Library in detail and install additional developer tools to really get going.

About the Author

  • Vincent van der Leun

    Vincent van der Leun is a software engineer living in the city of Utrecht in The Netherlands. Programming since the age of 8, he has worked with many different languages and platforms over the years. Rediscovering Java a few years ago, he loved it so much that he became an Oracle Certified Professional, Java 7 Programmer, and started the JVM Fanboy blog. Currently he works for CloudSuite, a company specializing in modern ecommerce solutions. At CloudSuite he works on various backend systems and web services, writes JavaScript code for frontend applications, supports consultants by providing complex SQL queries, and consumes coffee while having design-related discussions with fellow developers.

    Browse publications by this author
Book Title
Unlock this book and the full library for FREE
Start free trial