Kotlin through the eyes of a Java developer

Hello, Habr! Today I want to tell you about my experience of interaction with the Kotlin language.

Let me introduce myself – I am a Java developer, I work at a large bank, I create (and support existing) microservices.

A small remark: I'm not going to become an Android developerneither now nor in the future, so when I became interested in a new language, I did not take into account the arguments about the various conveniences of mobile development on it, and was guided only by the convenience of the language as a whole for the backend.

So, why did I decide to learn Kotlin? Well, first of all, everyone's been buzzing about it, saying it's about reducing the amount of code, conciseness, readability and sugar.

Secondly, Kotlin is fully compatible with Java. For understanding, below is a screenshot of what the contents of one of the intellij packages of the git4idea plugin looks like:

a piece of the git4idea plugin source structure

a piece of the git4idea plugin source structure

(Java and Kotlin classes are mixed together, but the code remains readable (in places))

Yes, projects can be migrated from Java to Kotlin gradually, which is a killer feature in my opinion.

So I thought, why not.

Here are the things I've gotten used to in my six months of pet-designing in Kotlin, and which I personally find convenient to use:

  1. Classes and methods closed for extension. Everything is final by default, unlike Java, where, on the contrary, any class is open for extension. Kotlin adheres to the principle described in Effective Java by J. Bloch, which sounds like this:

    “classes and methods should be private and not be extended or overridden unless we have a compelling reason to extend or override them. And when we decide that our class should be open for extension, we should document the consequences of overriding any method.” Source

    In order to make a class in kotlin extensible, we must use the open keyword, which does not have the transitive property – this means that when Class2 inherits from open class Class1, we cannot inherit Class3 from Class2 without adding the open keyword to it.

  2. Null-safe. The ability to allow/disallow (by default) assignment of null to a variable at the compile level is a plus.

    var a: String = "abc"
    a = null                // compile error
    
    var b: String? = "xyz"
    b = null                // no problem
  3. Getters and setters by default. Any field is not directly retrieved, moreover, under the hood it is private (by default) and can only be accessed by a getter. When you try to get a field of your object, you actually call the get method

    val isEmpty: Boolean

    is equivalent to the following Java code:

    private final Boolean isEmpty;
    
    public Boolean isEmpty() {
        return isEmpty;
    }

    Well, and:

    var someProperty: String = "defaultValue"

    also equivalent to:

     var someProperty: String = "defaultValue"
        get() = field
        set(value) { field = value }

    If desired, you can, of course, restrict access to getters or setters:

     var isEmpty: Boolean = true
        private set 

    Examples commodified

  4. One-line methods. I'll be brief: when declaring a method like:

     fun doSomething(): String{
        return “doing something”
    } 

    Kotlin allows you to remove the explicit return type declaration, curly braces, and the return keyword

    It starts to look like this:

     fun doSomething() = “doing something”

    At first it was unusual (especially when such declarations come after the class field initialization block), but then I got used to it

  5. Simplified large nesting when comparing strings and any other complex types in If – guys sewn up equals V == (starts working if you override the equals method explicitly, or if you mark the class with the data keyword.)

    I'll add that if you really need to compare references to objects, Kotlin has a separate operator for this: triple equals ===

    Java:

     if (str1.equals(str2.equals(errString)? "default":str2)){
         //... 
     }

    Kotlin:

     if (str1 == if (str2 == errString) "default" else str2) {
       //... 
     }

    Comparison of classes:

    Java:

    @EqualsAndHashCode
    @AllArgsConstructor
    public class MyClass {
       private String name;
       private int value;
     }
    
     
    public static void main(String[] args) {
      MyClass first = new MyClass("name", 5);
      MyClass second = new MyClass("name", 5);
      System.out.println(first.equals(second));//true
      System.out.println(first == second);//false 
    }

    Kotlin:

    data class MyClassK(var name: String?, var value: Int)
    
    fun main() {
      val first = MyClassK("name",10)
      val second = MyClassK("name",10)
      println(first == second)//true
      println(first === second)//false
    }

  6. Named arguments. When combined with default arguments, named arguments eliminate the need to use Builders (Builder pattern)

    Instead of the following Java code:

    @Builder //lombok annotation
    public class MyClass {
      private String name;
      private int value;
      private double rating;
    }
    
    MyClass myClass = MyClass.builder()
       .name("MyJavaObj")
       .value(10)
       .rating(4.99f)
       .build();

    In kotlin we can do this:

    class MyClassK(name: String? = null, value: Int = 0, rating:Double = 0.0)
    
    fun main() {
      var myClassK = MyClassK(value = 10, name = "MyKotlinObj", rating = 4.99)
    } 
  7. Operator Overloading

    Kotlin has a predefined set of operators that can be overloaded to improve readability:

    data class Vec(val x: Float, val y: Float) {
        operator fun plus(v: Vec) = Vec(x + v.x, y + v.y)
    }
    
    val v = Vec(2f, 3f) + Vec(4f, 1f)

    example borrowed from here

  8. Extension Functions

    In short: we can supplement existing classes with our functionality without inheritance, and treat this functionality as native throughout the rest of the program.

    fun String.format() = this.replace(' ', '_')
    
    val str = "hello world"
    val formatted = str.format()

    or, for example:

     val result = str.removeSuffix(".txt")

    (In Java I would put this in a static method of some StringUtils class)

    Convenient, isn't it? Especially since the IDE suggests our function among other pop-ups.

  9. Working with NPE in a call chain. If we want to extract some small part from a complex, composite object, but there is no guarantee that null will not be waiting for us on this path, we have to do the following:

    Java:

     try{
       Status status = journal.getLog().getRow(0).getStatus();
       if(status == null)
         throw new NullPointerException(“null status detected in log”);
     } catch(NullPointerException e){
       status == Status.ERROR;
       //logger.error(“Journal is not correct”);
     }

    Kotlin:

    var status: Status? = journal?.log?.row?.status
    status  =  status ?: Status.ERROR

    or

    var status: Status? = journal?.log?.row?.status
    if(status.isNull()){
      status  = Status.ERROR
      logger.error(“Journal is not correct”)
    }

I didn't touch on coroutines here, because I'm not familiar enough with them, and in general I don't have much experience working with multithreading, so I can express my expert opinion. But in general, I watched the report and I advise you to do the same. Maybe I'll get around to it, study the topic and write something about it.

How wonderful it is that now I don't have to worry about NPEs in the call chain. In Java, as you can see, you have to wrap the unsafe call chain in a cumbersome try-cath, but damn, sometimes I want null to be simply assigned to a variable when null is returned somewhere in the chain. This is exactly the functionality that kotlin gives me.

Lyrical conclusion:

In general, I am in deep thought about the language: I have been programming in Java for a very long time and it is somehow more familiar to me, or something.
(When I see files with functions in a project, separate from classes, I periodically twitch.)

There are many nuances and changes, no matter how you look at it, although there is compatibility.
I recently listened to several podcasts with guests – JetBrains developers and product managers, the guys were so enthusiastic about the language, its prospects and horizons, like Java, only better, Kotlin Multiplatform, etc.

But it still remains a fact that Kotlin didn't go to the backend en masse, but occupied the mobile niche, although maybe I don't know something. Maybe behind my back everyone in production has already secretly rewritten their toad projects in Kotlin, and they didn't tell me.

And is there such a thing as a main language? Maybe we shouldn't treat it that way, but stick to the principle of “a tool for every task”. But then for which backend tasks is kotlin, and for which java? Or is it “pumped-up java”?

I would really like to hear other people's experience of interacting with these languages ​​in the comments, preferably from guys who work on the backend in the bloody enterprise, and not mobile developers, although I am glad to hear from everyone.

Have you had any experience migrating large projects from one language to another? If yes, which one? If no, why not?

Maybe you personally changed your main language? Why? Sugar and lack of semicolons? Or more global reasons?
(It occurred to me that syntactic sugar doesn't seem like a killer feature of any language, because when you're used to writing like that, and others are used to reading like that, it ceases to be a problem)

Sources:

https://habr.com/ru/companies/otus/articles/532270/ – Equality testing in Kotlin

https://youtu.be/rB5Q3y73FTo?si=piObKnscuv1S9vtg – Roman Elizarov. Coroutines in kotlin

https://habr.com/ru/companies/vk/articles/329294/ – kotlin features overview from VK

https://stackoverflow.com/questions/37906607/getters-and-setters-in-kotlin – getters and setters. Thread on Stack Overflow

https://www.baeldung.com/kotlin/open-keyword – keyword open

https://github.com/amitshekhariitbhu/from-java-to-kotlin – cool repo-training in Java vs Kotlin format

https://radioprog.ru/category/183 – design patterns, without the fluff

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *