Test-Driving Kotlin in ZK
Let's see what kinds of benefits Kotlin offers over Java when working with ZK as your frontend framework in a Java EE application.
Join the DZone community and get the full member experience.Join For Free
Every now and then it's time to learn something new — especially for a software developer. Be it just to keep in touch with recent developments, to challenge yourself by getting out of your comfort zone, or to widen your perspective and get fresh ideas on something you already know.
Very often, this means trying out the latest framework or a new platform. Sometimes, you work on an existing project and several decisions are already set in stone.
Assume you work on a JEE application using ZK as the frontend framework running on the JVM. In this case, you might not have all the degrees of freedom you might like. Still, you can be cheeky and keep up to date by using an alternative programming language running on the JVM. Obviously, a major feature of this language should be the interoperability with existing Java code and libraries.
Kotlin claims to be compatible. So, I decided to put this to the test just to see how far I could get and what surprises it has to offer. (Mild spoiler: It works!) Other important features of Kotlin are shorter and more expressive syntax as well as a safer and more flexible type system, avoiding common mistakes.
As a positive side effect, learning Kotlin adds to your own skill set — for example, when it comes to coding for the Android Platform in a future project (you never know!).
So why not combine the pleasant with the useful and just...
After that, .kt (Kotlin) files are compiled automatically and produce bytecode executable by the JVM. Existing Java code continues to compile as well.
The usual Gradle commands just work as before:
./gradlew compileKotlin(just compile Kotlin)
./gradlew test(run all tests)
./gradlew build(e.g. build the WAR file)
That's all we need for now to move on.
Assume we have the following ZUL-Page, implemented using the MVVM pattern:
It will display a
<textbox> widget to enter your name. When pressing ENTER ("onOK") or clicking the button, it will trigger the "submit" command and display a response:
"Hello [yourname]!" in the
<label> below. What we need to implement is a View Model class with 2 properties:
In Java, this implementation could look like this:
Nothing remarkable here... just a POJO with a command handler - for the
As a first surprise, the same class in Kotlin becomes:
One initially obvious benefit (from a Java developer's point of view) is the more compact/expressive syntax avoiding the (often brainlessly IDE-generated) getters and setters. Even in this embarrassingly trivial ViewModel class, we save about half the lines of code.
Astonishingly (not for a Kotlin developer) the compiled class still works in a ZK application when used as an MVVM ViewModel (without having to change the ZUL code or data- or command-binding expressions). Designed for interoperability with Java, the Kotlin compiler conveniently adds the getters/setters automatically, allowing existing frameworks to work as before, leveraging their functionality.
var(line 2) stands for variable, hence it has an implicit getter and setter
val(line 3) is a read-only value, which needs just a getter
Where needed, we can implement a custom getter as for the calculated property
response (which, as a bonus, uses the string interpolation feature to insert
$name into the response string).
So far, we have simply achieved the same thing with less code (bad luck if you're paid by lines of code — which I think is just an urban legend anyway. Less typing for everyone else).
Other language features help to create code that is more compile/refactoring safe (without writing more code). Especially in a ZK+MVVM application, this can help to avoid runtime errors — often occurring due to a failed reflective method or field invocations.
In particular, I am referring those 2 annotations using magic strings that are resolved via reflection during runtime. As we can see in the code below, the command name and the notifyChange property name are such unwanted hard-coded String values:
The @Command method is used in the ZUL-File in a @command-binding:
To avoid repetition, the command name is optional in the @Command annotation and, if missing, matches the method name (this also works in Java but doesn't solve our problem entirely).
Still, if the method name changes due to refactoring, the UI will break at runtime — no compiler will help there. In order to be more flexible with refactoring changes, we can use Kotlin's nice reflection API to introduce a property value, which will always match the method name.
In a similar way, we can use the reflection API to avoid hard-coding the
response-property name (I'll get to that in a second). Here is the safer version of our View Model Class:
... and the updated usage in the .zul file (mySafeIndex.zul):
The syntax might look a bit alien at first — but hey, that's part of the learning experience. (Just try something similar in Java and you'll quickly learn to love this feature.)
As we can see, we traded the hard-coded Strings for compile- and refactoring-safe values, and we can use them in the ZUL file to never break the ZUL file due to command method name changes in the View Model.
Now, what happened to
@NotifyChange("response")? Unfortunately, we can't just say
@NotifyChange(::response.name) — that would have been nice, but annotation values require compile-time constants, while reflection is still a runtime API (both in Java and Kotlin).
Luckily, ZK has an (arguably ugly) utility method to trigger @NotifyChange programmatically ... We just have to call:
Still too much to write? Kotlin's Extensions help to reduce repetitive typing. On top, I'd argue that the first 2 parameters are
null 99% of the time, so we can implement the following 2 extension functions, both doing the same thing:
notifyChange extension functions are defined on the Any type, so they can be called on every object. I can only recommend reading/understanding the documentation about this feature. Excessive use of this feature might make your code harder to reason about.
Here, it achieves a shorter syntax; fitting the existing ZK API to our needs. The following 4 lines have the same effect (you can choose which you like most):
You can also notify nested properties, e.g. the
street-property of an
Before I forget to mention it: In order to use Kotlin's reflection API, you have to add the kotlin-reflect dependency.
Taking a Break
Since the article is already getting quite long, I'll cover a slightly more interesting CRUD example using ZK's Form-Binding in combination with Kotlin's data-classes and Kotlin+JUnit+ZATS in separate articles.
Special thanks go to Carsten Lenz (Germany)
who patiently helped out during a "quick" 20-30-minute call
(which actually took 2 hours)
The full code is available on GitHub and can be run using the following commands:
Opinions expressed by DZone contributors are their own.