Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Kotlin and Java EE (Part 3): Making It Idiomatic

DZone's Guide to

Kotlin and Java EE (Part 3): Making It Idiomatic

This look at the relationship of Java EE and Kotlin examines how to make use of Kotlin's operators, nullability, and Optionals to the greatest effect.

· Java Zone
Free Resource

Managing a MongoDB deployment? Take a load off and live migrate to MongoDB Atlas, the official automated service, with little to no downtime.

Converting Java EE applications to Kotlin started with the battle with the framework, where we successfully outmaneuvered all the obstacles presented by sometimes-archaic standards. In the process, the code was enriched with modern, Kotlin-specific constructs, making it concise and safer.

If you did not read the previous two parts of the series, you can find them here:

  1. Kotlin and Java EE: Part One - From Java to Kotlin
  2. Kotlin and Java EE: Part Two - Having Fun with Plugins

After briefly revisiting already made changes, I will add some final touches.

What We Already Did

Many constructs from the previous two parts are already idiomatic Kotlin. Let’s take a look at the set definition:

private final Set<Class<?>> classes =
    new HashSet<>(Arrays.asList(KittenRestService.class));


As Java does not support a simple construction of Set and some other collections from a list of objects, we have to go to the Arrays class to create a List (!), and then we convert it to Set. In Kotlin, it becomes:

private val classes = setOf(KittenRestService::class.java)


We also converted Java Beans to Kotlin data classes, making them much shorter in the process. We got rid of all getters and setters, and got equals()hashCode(), and toString()  for free.

@Entity
data class KittenEntity private constructor(
        @Id
        var id: Int?,
        override var name: String,
        override var cuteness: Int // set Int.MAX_VALUE for Nermal
) : Kitten {

    constructor(name: String, cuteness: Int) : this(null, name, cuteness)
}


Thanks to compiler plugins, we could fake immutable objects without a parameterless constructor:

@Path("kitten")
class KittenRestService 
    @Inject constructor(private val kittenBusinessService: KittenBusinessService) {


The lateinit keyword made handling of values initialized by the framework a bit easier, as we could avoid unnecessary null checks:

@Stateless
class KittenBusinessService {

    @PersistenceContext
    private lateinit var entityManager: EntityManager
    ...


Let’s see what else can be improved.

Null or Optional?

This is a pretty tough question. Kotlin has excellent support for nullable values, which helps a lot when you are using third party libraries. The question is what to use when you have the opportunity to choose one over the other? Here is our original Optional producer and consumer pair:

fun find(id: Int): Optional<KittenEntity> =
    Optional.ofNullable(entityManager.find(KittenEntity::class.java, id))

fun find(id: Int): KittenRest = 
    kittenBusinessService
        .find(id)
        .map { kittenEntity -> KittenRest(kittenEntity.name, kittenEntity.cuteness) }
        .orElseThrow { NotFoundException("ID $id not found") }


Idiomatic Kotlin solution would use nulls, so it becomes:

fun find(id: Int): KittenEntity? =
    entityManager.find(KittenEntity::class.java, id)

fun find(id: Int) = 
    kittenBusinessService.find(id)
        ?.let { KittenRest(it.name, it.cuteness) }
        ?: throw NotFoundException("ID $id not found")


Nullable values can appear in each step of the call chain, so you have to use the question mark for all calls. That solves the nullability issue, but it is not pretty.

However, if the return type is Optional and the result becomes  Optional.empty, all future monadic calls on that object will be simply skipped and the result will be Optional.empty. To me, this looks like a cleaner solution, and it is also a safer one if you plan to call Kotlin code from Java. For Java interop, prefer Optionals over nulls.

Operators!

findadd and  delete  are perfectly valid names for methods, but wouldn’t it be good to use operators instead?

Method

Operator

service.find(id) service[id]
service.add(kittenEntity) service += kittenEntity


I find it not to be just shorter, but also more readable, as code is not a big pile of method calls anymore. Be careful to use only well-known and well-understood operators, otherwise, you will get a big mess like some Scala libraries, and then you will need an operator periodic table. In case of a data repository, the MutableMap-like interface works nicely. Note that I used the “plus assign” (+=) operator for persisting an entity, as the original collection contains what it already has, plus an additional item.

Here is how to declare them:

operator fun plusAssign(kitten: KittenEntity) = 
    entityManager.persist(kitten)

operator fun get(id: Int): KittenEntity? = 
    entityManager.find(KittenEntity::class.java, id)


You may want to leave the original methods and make an operator wrapper around them, as original methods can return values, while some operators cannot. Other good candidates for operators are “remove” and “contains” methods, because they can be represented with “minus assign” (-=) and Kotlin’s in operator. I will leave the rest to your imagination.

Conclusion

The purpose of writing in an idiomatic way is to have more readable and safer code, and I hope that the presented example succeeded in that intention. This series shows just a couple of ways to improve code over the Java version while leaving some areas untouched. Features worth exploring are: extension functions, and if, when, and try/catch as functions. Just explore, find out what works for you, and have fun!

The complete code can be found here.

MongoDB Atlas is the easiest way to run the fastest-growing database for modern applications — no installation, setup, or configuration required. Easily live migrate an existing workload or start with 512MB of storage for free.

Topics:
kotlin ,java ee ,java ,operators ,tutorial

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}