Kotlin and Java EE: Part One - From Java to Kotlin
One 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 to. Luckily, all those issues could be avoided
Our target for conversion is a simple Java WAR that can store and retrieve records from the database over REST interface. To get started, pull the fables-kotlin repo from GitHub. The project is in the jee/java
directory. If you like to run and test it, check the instructions on the GitHub.
Making Kotlin classes Java EE friendly with your own hands.
Java EE servers can be very picky about the way classes are structured. They mostly must be non-final with a parameterless constructor and public methods. The parameterless constructor is needed for instantiation and 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; requirements a similar to how typical Java class looks like anyway, but Kotlin does things a bit differently.
Add Kotlin to Build Script
Before converting anything, add 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 Kotlin compiler plugin, apply it to the module, and add some dependencies. While Kotlin Standard Library is not mandatory, it provides a huge number of useful functions for a small penalty in size.
Easy start: RestApplication class
Intellij has built-in support for conversion of Java classes to Kotlin. It is quite easy to use it: you can either open Java class and press [CTRL]+[ALT]+[SHIFT]+K, or you can copy a piece of code from Java file and paste it into 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 converted code will work fine, it is still a Java-style code, but in a different language. Replace complicated Java HashSet initialization with 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 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 a class had a parameterless constructor and setters, frameworks would instantiate it and populate it with setters. In this case, a framework must use parametrized constructor. As Java constructor does not provide parameter names, automatic binding is not possible. That is why are there @JsonProperty
annotations. Parameter name metadata can be enabled in Java 8 with 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 default constructor, which is part of class declaration. Each parameter declared as val
or var
will become class property with getter function. var
will also get a setter. If we declare 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 JavaBean with property person of type Person. Add getter, setter, equals, hasCode and toString methods, and a 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. Kotlin class has the same functionality as Java class, and more, in just four nicely formatted lines of code.
JPA Entity Class
Now when we warmed up, let’s try something more interesting: JPA Entity class. It is a typical long sequence of getters and setters finished by 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 the [Kotlin] once more. Now declare it as data class. Declare primary constructor as private. Put all field declaration in, remove defaults and add override. Delete all methods (but leave the constructors!). Change secondary constructors to call 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)
}
We are still stuck with a parameterless constructor. Let’s take a look at 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. Empty string is a simple but cheap workaround.
Business service
KittenBusinessService.java is simple 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, entityManager
declaration is all wrong:
protected var entityManager: EntityManager? = null
// ...
entityManager!!.persist(kitten)
As 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 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, proxy cannot be created. We will also do a small fix of returned id type because after an entity is persisted, id is not null anymore. !!
means "value should not be null; if it is, throw an exception ". 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))
}
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 strange runtime behavior.
REST Service
Start with automatic conversion again. Make the class and all non-private members open. Now try to use 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 getter and setter, and that everything in Kotlin is final. Protected variable kittenBusinessService
created 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 makes 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 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.
Comments
Post a Comment