Book Image

Introduction to JVM Languages

Book Image

Introduction to JVM Languages

Overview of 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.
Table of Contents (21 chapters)
Title Page
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

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.