» » Comparison of Kotlin and Java when writing backend applications

Comparison of Kotlin and Java when writing backend applications

I am a developer of large and small backend systems in large companies. I had to write both separate services that communicate with other backend services of different levels of depth, and services that work in conjunction with the front. Previously, I used Java + Spring to write code.

Comparison of Kotlin and Java when writing backend applications

And then I changed the project and ran into Kotlin in the backend. And I want to share the advantages of Kotlin, the differences between Kotlin and Java in absolutely the same tasks.

Looking ahead, I’ll say that there is no colossal difference, because of which you urgently need to rewrite everything in Kotlin. But there are a huge number of features that make development faster, easier and safer. On the current project, the team and I write all the new functionality in Kotlin, while simultaneously rewriting old pieces of Java code from n years ago. On Kotlin, these pieces are much more readable and short.

Ease of integration into an existing project written in Java

If you are only looking at Kotlin for the backend, then keep in mind that in the environment that launches your Java 8 project, you can run a compiled Kotlin project without dancing with a tambourine. Yes, on the same jvm, on the same environment and with a minimum of effort.

It was a discovery for me that even within the same application there can be classes in Java and Kotlin. All the magic happens at compile time. Depending on the settings, you can specify what to build first: Kotlin classes or Java classes.
Compilation of Kotlin sources to Java LTS bytecode is now available - 8, 11 and (still experimental) 16.

Initialization and logic of working with DTO classes


The example is hackneyed, but as clear as possible.

data class Cat(val name: String, val color: String, val height: Int)

And now the same in Java:

public class Cat {
    private final String name;
    private final String color;
    private final Integer height;

    public Cat(String name, String color, Integer height) {
        this.name = name;
        this.color = color;
        this.height = height;
    }

    public String getName() {
        return name;
    }

    public String getColor() {
        return color;
    }

    public Integer getHeight() {
        return height;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Cat cat = (Cat) o;
        return Objects.equals(name, cat.name) && Objects.equals(color, cat.color) && Objects.equals(height, cat.height);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, color, height);
    }
}

It seems to me that even comments are unnecessary here.
Data class pointers in Kotlin by default imply getter, setter (for var fields), equals, hashcode, toString for all fields. If you wish, you can override each of these methods in your own way, but this is extremely rarely required.

class Cat(val name: String, val color: String, val height: Int) {

    override fun toString(): String = "overridden toString"
}


Null safety

In Kotlin, it is required to explicitly indicate whether a particular method can return null or not. Thus, we can assume that all data is already wrapped in an Optional analogue. And NullPointerException will meet you so rarely that you will have time to miss it.

fun saveReviewNullable(r: Review): Review? = reviewRepository.save(r)

fun bar(r: Review) {
    val savedReviewNullable: Review = saveReviewNullable(r)!! // Есть риск NPE - не феншуй
    val savedReviewNotNull: Review = saveReviewNullable(r) ?: Review() // феншуй
}


Highlighting the main constructor

The bottom line is: there is a main (Primary) constructor and auxiliary (Secondary). Auxiliaries are required to call the main one as the constructor of the parent class.

class Cat(val name: String, val color: String, val height: Int) {

    constructor(name: String) : this(
        name = name,
        color = "fixed color",
        height = 10
    )
}


Explicit declaration of mutable and immutable fields


The next advantage: in Kotlin, quite simple and elegant constructions are obtained. If it is required that the dto field can be changed, then var is used to declare it. Then the setter method will be created and the field will not be final.
And if you want to make the field immutable, you should use val for the declaration. Looks very nice and simple. Plus, you don't have to follow the helper methods.
Example: the color and height fields can be changed after creation, but the name can only be changed when the object is initialized:

data class Cat(val name: String, var color: String, var height: Int)


Immutable collections by default.


What appeared in Java a little later was already in Kotlin for a long time - the creation of collections immediately immutable.

val list = listOf("one", "two")
val map = mapOf(1 to "one", 2 to "two")
val set = setOf(1, 2 ,3)

Any changes to these collections will create a new immutable collection after the conversion:

val list = listOf("one", "two")
val list2 = list.plus("three")

But changing some element separately will not work. For classic mutable collections, explicitly mutable counterparts are used:

val list = mutableListOf("one", "two")
val map = mutableMapOf(1 to "one", 2 to "two")
val set = mutableSetOf(1, 2 ,3)


Working with Complex Classes with Primitive Methods


The advantage of Kotlin, which I never cease to rejoice at, is the ability to use operators for basic operations on complex classes. If you need to add BigDecimal numbers, you take and write them through a plus. You don't need to explicitly call the method on the first addend.
The same with arrays: if you want to remove an element from a mutable array, you write an array minus this element. And if the element is present, then it is removed

val a = BigDecimal(1)
val b = BigDecimal(2)
val sum = a + b

In Java, you need to call a special method:

BigDecimal a = new BigDecimal(1);
BigDecimal b = new BigDecimal(2);
BigDecimal sum = a.add(b);

Similar tricks work with more complex classes, such as collections:

val list = listOf("one", "two") - "one" // list - коллекция из элемента "two"


It is possible to write one-line methods really in one line


If the method is simple and consists of one operation or a chain of operations written in one line, then it is not necessary to write curly braces and return. Directly write:

fun getReviewByTitle(title: String): List<Review> = reviewRepository.getAllByTitle(title)

Instead of Java option:

public List<Review>(String title) {
        return reviewRepository.getAllByTitle(title);
    }


context functions.


Interesting moves towards functional programming in the spirit of context extraction: lambdas can be twirled as you like.
There are functions let, apply, also, with, run. Because of their abundance, the question first arises: what is suitable for a particular case. But when you get used to it, it becomes incomprehensible how you used to live without them.
A simple example: take the result and process it somehow:

fun saveReview(review: Review): Review = reviewRepository.save(review)

fun bar(r: Review) = saveReview(r).let { it.comment + it.title }

Or initialize the object and additionally initialize its var fields:

class Cat(val name: String, val height: Int) {
    var color: String? = null
}

fun bar() = Cat("Fred",10).apply { color = daoService.getPopularColor() }

Someone may say that this is sugar sugar and they will be right. But I’ll say for myself: if a large number of template things enter the language, and you don’t need to constantly think about them, the development process becomes easier, the number of errors is less. But you need to think about the style code more often, because without it you can do a lot 🙂

Now I still continue to write and see code in Java, but in 99% of cases this is due to the educational programs in which I participate.
I advise everyone to try it - at least on a pet project, to understand whether the Kotlin paradigms suit you or not.

Related Articles

Add Your Comment

reload, if the code cannot be seen

All comments will be moderated before being published.