Kotlin and Java EE (Part 1)
Kotlin and Java are both JVM languages, so it should be easy to convert from one to the other, right? Not quite. Making Kotlin classes JEE-friendly takes a bit of work.
Join the DZone community and get the full member experience.
Join For FreeOne of the main strengths of Kotlin is good Java integration. As it is fairly easy to convert Java to Kotlin, it seems that making Java EE applications in Kotlin should be a no-brainer. However, there are some subtle differences between the two that make conversion tricky:
- While most frameworks require non-final classes, Kotlin classes are final.
- Injection will introduce a lot of unnecessary null checks.
- Both of these and mandatory parameterless constructors will ruin attempts to write functional-style code.
Java EE and Kotlin are not really best friends unless you make them. Luckily, all those issues can be avoided.
Our target for conversion is a simple Java WAR that can store and retrieve records from a database over a REST interface. To get started, pull the fables-kotlin repo from GitHub. The project is in the jee/java
directory. If you would like to run and test it, check the instructions on the GitHub.
Making Kotlin Classes Java EE Friendly
Java EE servers can be very picky about the way classes are structured. They mostly must be non-final with parameterless constructors and public methods. A parameterless constructor is needed for instantiation, and the other two requirements are for making proxies. Proxies intercept calls to the object and enrich them with additional functionality. When you write Java code, you do not think about it too much, but Kotlin does things a bit differently.
Add Kotlin to the Build Script
Before converting anything, add the Kotlin compiler to the build script. It means going from this:
apply plugin: 'war'
description = 'Java Reference Server'
to this:
plugins {
id "org.jetbrains.kotlin.jvm" version '1.1.1'
}
apply plugin: 'kotlin'
apply plugin: 'war'
description = 'Java Reference Server'
ext.kotlin_version = '1.1.1'
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
We have to register the Kotlin compiler plugin, apply it to the module, and add some dependencies. While the Kotlin Standard Library is not mandatory, it provides a huge number of useful functions for a small penalty in size.
Easy Start: The RestApplication Class
IntelliJ has built-in support for converting Java classes to Kotlin. It is quite easy to use it: You can either open a Java class and press [CTRL]+[ALT]+[SHIFT]+K, or you can copy a piece of code from Java file and paste it into the Kotlin one. In both cases, the code will be automatically converted. I will call this key combination [Kotlin] from now on.
Open the RestApplication.java class. Press [Kotlin]. Done.
While the converted code will work fine, it is still a Java-style code, but in a different language. Replace the complicated Java HashSet initialization with an immutable Kotlin set and make getClasses a real function — and you are good to go:
@ApplicationPath("api")
class RestApplication : Application() {
private val classes = setOf(KittenRestService::class.java)
override fun getClasses() = classes
}
Convert the Interface
Press [Kotlin] and move on.
Converting the Simple Immutable Class
As a next step, we will convert fables.kotlin.jee.java.rest.KittenRest.java class. Note that class has single constructors with assignments for all properties and no setters. This class is used just for transferring data over REST, so it is made immutable. If the class had a parameterless constructor and setters, frameworks would instantiate it and populate it with setters. In this case, the framework must use a parametrized constructor. As a Java constructor does not provide parameter names, automatic binding is not possible. That is why there are @JsonProperty
annotations. Parameter name metadata can be enabled in Java 8 with a special compiler parameter, but by default, it is off.
Press [Kotlin] and IDEA will convert the class to Kotlin for you. Magically, the whole body of the class disappears.
class KittenRest (
@param:JsonProperty("name") val name: String,
@param:JsonProperty("cuteness") val cuteness: Int
)
Kotlin classes can have a default constructor, which is part of the class declaration. Each parameter declared as val
or var
will become a class property with a getter function. var
will also get a setter. If we declare a class as data class
, it will also get hasCode(), equals(), toString, and some other, Kotlin-specific methods.
Now take a break and do a little exercise:
Create a JavaBean with a person property of type Person. Add the getter, setter, equals, hasCode, and toString methods, and the constructor that accepts person. Now count how many times you had to write “person”, in either upper or lower case.
I hope that you were not too shocked with the result. The Kotlin class has the same functionality as Java class, and more, in just four nicely formatted lines of code.
JPA Entity Class
Now that we are warmed up, let’s try something more interesting: a JPA Entity class. It is a typical long sequence of getters and setters finished by a huge equals
, hashCode
, and toString
. I will put just a short excerpt to remind you how bloated it is.
@SequenceGenerator(name = "kittens_id_seq", sequenceName = "kittens_id_seq", allocationSize = 1)
@Entity
@Table(name = "kittens")
public class KittenEntity implements Kitten {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "kittens_id_seq")
private Integer id;
private String name;
private int cuteness;
protected KittenEntity() { }
public KittenEntity(String name, int cuteness) {
this.name = name; this.cuteness = cuteness;
}
public Integer getId() {
return this.id;
}
...
}
Here we have a first good chance for real improvement. Hit [Kotlin] once more. Now declare it as a data class. Declare the primary constructor as private. Put all field declarations in, remove defaults and add an override. Delete all methods (but leave the constructors!). Change the secondary constructors to call a primary constructor. Look how lean it looks like:
@SequenceGenerator(name = "kittens_id_seq", sequenceName = "kittens_id_seq", allocationSize = 1)
@Entity
@Table(name = "kittens")
data class KittenEntity private constructor(
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "kittens_id_seq")
var id: Int?,
override var name: String,
override var cuteness: Int // set Int.MAX_VALUE for Nermal
) : Kitten {
protected constructor() : this(null, "", 0)
constructor(name: String, cuteness: Int) : this(null, name, cuteness)
}
But we are still stuck with the parameterless constructor. Let’s take a look at the mandatory name
property. The value will be provided either through a public constructor or by JPA. However, JPA will first construct the object and then set the value, which means that we must provide some default for the construction. An empty string is a simple but cheap workaround.
Business Service
KittenBusinessService.java is simple to service that can insert and read data. After converting it to Kotlin, we have to do a couple of tricks to make it work.
For a start, the entityManager
declaration is all wrong:
protected var entityManager: EntityManager? = null
// ...
entityManager!!.persist(kitten)
As the value will be injected after the class is constructed, it has to be declared as nullable with null initial value, requiring null-checks on all calls. Luckily, Kotlin has a solution for variables that will get their non-null values before their first use: lateinit
It means that there is no value right now, but there will be one later, and that is exactly what injection does. That allows us to declare it as non-nullable and throw away all null-checks.:
protected lateinit var entityManager: EntityManager
// ...
entityManager.persist(kitten)
Class and all non-private methods must be declared open — otherwise, the proxy cannot be created. We will also do a small fix of the returned id type because after the entity is persisted, id is not null anymore. !!
means “value should not be null; if it is, throw an exception". The method find
is just a function, so we will declare it that way.
@Stateless
open class KittenBusinessService {
@PersistenceContext protected lateinit var entityManager: EntityManager
open fun add(kitten: KittenEntity) = {
entityManager.persist(kitten)
return kitten.id!!
}
open fun find(id: Int): Optional<KittenEntity> =
Optional.ofNullable(entityManager.find(KittenEntity::class.java, id))
}
The method return type is inferred by the compiler. However, I prefer declaring it anyway. If you make a mistake and return the wrong type, the declaration will catch that and you will get a compiler error rather than some strange runtime behavior.
REST Service
Start with automatic conversion again. Make the class and all non-private members open. Now try to use the service. It will fail:
Caused by: org.jboss.weld.exceptions.UnproxyableResolutionException:
WELD-001480: Bean type class fables.kotlin.jee.java.rest.KittenRestService is not proxyable because it contains a final method
protected final void fables.kotlin.jee.java.rest.KittenRestService.setKittenBusinessService
(fables.kotlin.jee.java.business.KittenBusinessService) - <unknown javax.enterprise.inject.spi.Bean instance>.
Remember that each class property automatically creates a getter and setter, and that everything in Kotlin is final. The protected variable kittenBusinessService
created a final protected setter, and Weld did not like that because it cannot proxy it. Here we can either make it open or private, it will work in both cases. As it is meant to be used privately, let’s make it private, and let’s make it lateinit
so we can clear up the null-checks:
@Inject
private lateinit var kittenBusinessService: KittenBusinessService
Conclusion
The coolest thing about Kotlin is how well it interoperates with Java. I could convert class by class and everything would still work.
Kotlin and Java have a couple of differences in their defaults; Kotlin’s classes and methods are final, while Java’s are open, and Kotlin’s members are public, while Java’s are protected. That does not make much difference when you write standalone programs, as the compiler will warn you about the problems, but it creates some issues with Java EE frameworks. You will not be warned about the problems until the application is deployed and used. Problems can be avoided with careful coding, but being careful with each new class is too error prone. Forget one open
and the application will fail.
In the next installment, I will show you how to make your Java EE application more robust to Kotlin specifics by employing Kotlin compiler plugins. They will also make code cleaner and more functional, and ultimately better than the Java counterpart.
Published at DZone with permission of Željko Trogrlić. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Understanding the Role of ERP Systems in Modern Software Development
-
Stack in Data Structures
-
IDE Changing as Fast as Cloud Native
-
Alpha Testing Tutorial: A Comprehensive Guide With Best Practices
Comments