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

Kotlinizing Java

DZone's Guide to

Kotlinizing Java

Here is a side-by-side approach to rebuilding a Spring Boot app in Kotlin, pointing out Kotlin's strengths and weaknesses when compared to Java.

· Java Zone
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

As a developer who works primarily with Java, the topic of Kotlin comes up frequently when reading online articles, blogs, etc. And like sugary drink advertisements, the subliminal effect has worked, so I thought I’d dive in and see if it’s something I would consider using on my next project.

Learning Approach

I am going to take a small Spring Boot application that I built with Java and rebuild it from scratch using Kotlin to see what I can learn.

If you want to see the end result, then you can find both GitHub repositories below:

Creating the Kotlin Version

Helpful IntelliJ

IntelliJ has a nice “Convert Java File to Kotlin” feature (under Code menu), which allows you to see how a Java file would look like in Kotlin.

In the beginning, I used it quite a lot to either get started or compare what I had written to what it produced.

It’s important to remember to always check the output before accepting it.

For example, Java constants were converted into a companion object. This may be fine, but if one decompiles it (Tools > Kotlin > Show Kotlin Bytecode > Decompile) then one can see a separate class being constructed with a get method just to return a constant. This seemed a bit of an overkill, and after a little more investigation I decided to create a top level const instead.

So rather than:

class FirstName(value: String) {
    val value: String

    init { 
        … 
    }

    private fun validate(input: String): String {
        … 
    }

    companion object {
        val MAX_LENGTH = 25
    }
}


I instead have:

const val FIRSTNAME_MAX_LENGTH = 25

class FirstName(value: String) {
    val value: String

    init { 
        … 
    }

    private fun validate(input: String): String {
        …
    }
}


Creating the Core Module

The use cases are slightly smaller in size as one can declare the constructor as part of the header, and are not null by default.

In the domain, the value objects (Age, FirstName etc) are slightly larger in size, as in the Java version Immutables are used. The Kotlin version, however, reduces dependencies and there's no need for code generation.

Initially, I also created the domain objects (Customer) as classes with business logic, toString, equals, and hashCode. However, this turned out to be very similar to using a data class with only business logic, which reduced the size and increased clarity.

data class Customer(val id: Id, val name: Name, val age: Age) {
    fun canPurchaseFireworks() : Boolean {
        return age.value > 21
    }
}


vs.


public final class Customer {

    private final Id id;
    private final Name name;
    private final Age age;

    private Customer(final Id id, final Name name, final Age age) { 
        ... 
    }

    public static Customer of(final Id id, final Name name, final Age age) {
        return new Customer(id, name, age);
    }

    public boolean canPurchaseFireworks() {
        return age.value() > 21;
    }

    public Id getId() { 
        ...
    }

    public Name getName() { 
        ... 
    }

    public Age getAge() { 
        ... 
    }

    @Override
    public boolean equals(final Object o) {
        ... 
    }

    @Override
    public int hashCode() { 
        ... 
    }

    @Override
    public String toString() { 
        ... 
    }
}


Creating the Data Module

There wasn't that much difference here, but I did come across the need to "open" classes when inheriting (CustomerEntity inherits from BaseEntity) as Kotlin classes (and methods) are final by default.

@MappedSuperclass
open class BaseEntity {
    @Version var version: Int = 0
}


Creating the Adapter Module

The ability to use data classes for the DTOs (using jackson-module-kotlin here as well) makes this an improvement on the Java version

data class NameDTO(@JsonProperty("firstName") val firstName: String,
                   @JsonProperty("middleName") val middleName: String,
                   @JsonProperty("lastName") val lastName: String,
                   @JsonProperty("suffix") val suffix: String) {
}


vs.


public class NameDTO {

    @JsonProperty("firstName")
    public final String firstName;
    @JsonProperty("middleName")
    public final String middleName;
    @JsonProperty("lastName")
    public final String lastName;
    @JsonProperty("suffix")
    public final String suffix;

    @JsonCreator
    public NameDTO(@JsonProperty("firstName") final String firstName,
                   @JsonProperty("middleName") final String middleName,
                   @JsonProperty("lastName") final String lastName,
                   @JsonProperty("suffix") final String suffix) {
        this.firstName = notBlank(firstName, "First name is required");
        this.middleName = middleName;
        this.lastName = notBlank(lastName, "Last name is required");
        this.suffix = suffix;
    }
}


Creating the Web Module

The constructor autowiring isn’t as nice (class header becomes a bit too long).

class ErrorHandlerController @Autowired constructor(private val errorAttributes: ErrorAttributes) : ErrorController { 
    ... 
}


However, since I am using Spring 4.3.x, I can exclude @Autowired as I only have one constructor, which improves readability.

class ErrorHandlerController(private val errorAttributes: ErrorAttributes) : ErrorController { 
    ... 
}


The need to use arrayOf in the request mappings isn’t as nice as the Java version where one can specify values with or without braces.

@RequestMapping(path = arrayOf("/api/customers"), produces = arrayOf(APPLICATION_JSON_V1_VALUE))


vs.


@RequestMapping(path = "/api/customers", produces = APPLICATION_JSON_V1_VALUE)


An improvement from Kotlin was in the ErrorHandlerController where a Map<String, Object> was being converted to Map<String, String>, and Kotlin was able to reduce it, to one, easier to read line, compared to the multiple operations in the Java version

return errorAttributes(request).mapValues { 
    it.value.toString() 
}


vs.


return errorAttributes(request).entrySet().stream()
                               .collect(toMap(Map.Entry::getKey,
                                              entry -> entry.getValue().toString()));


Creating the Configuration Module

Not much difference here between the versions except the need to use lateinit when doing property autowiring.

Then it was time to run the application.

  • First error:

Main method not found in class com.example.clean.app.MainApplication, please define the main method as: public static void main(String[] args)

After some investigation, the solution lay in the application.gradle file where the springBoot mainClass property has to end with Kt i.e. mainClass = 'com.example.clean.app.MainApplicationKt'.

  • After restarting I came across my second error:

@Configuration class 'AdapterConfig' may not be final. Remove the final modifier to continue.

Just like in the data module, final classes need to be opened, in this case @Configuration classes.

  • Another restart and I came across my third error:

@Bean method 'customerAdapter' must not be private or final; change the method's modifiers to continue

Ok I get it now, I need to open all @Bean definitions, @SpringBootApplication and @RestController

  • Now the server starts but the application fails with a long stacktrace and I realize that since I am using Spring Hateoas I have to open the functions as well.

Instead, I decided to investigate some more and discover that since I am using Kotlin 1.1.x, I can use the kotlin-spring plugin to avoid needing to add open all over the place.

Unit Testing

For unit testing, I decided to use KotlinTest.

It has a rich array of testing styles to choose from (so far I have only used the StringSpec and FunSpec).

In my Java projects, I like to use JUnitParam, so I was looking for something that had similar functionality, and KotlinTest’s table-driven testing feature allows for the same thing.

It also provides the ability to mock objects, using Mockito 2.

Conclusion

Kotlin’s non-null types, data classes and concise lambdas (and other interesting features that I have yet to use — like coroutines)  have made it a very appealing option.

Since I mostly develop new applications using Spring Boot, I tend to use Java.

However, my recent experience with Kotlin has brought it to my Java territory, a territory that was once indigenous to a single language, now under threat.

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
kotlin ,spring boot ,java ,tutorial ,app development

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}