You never touched Groovy, nor did you jump on the Scala train. Clojure never attracted you; and you heard about Ceylon long after the language had already died. You are one of those old-fashioned Java folks! But now, after all those years, you want to join the cool Kotlin kids. So, where to start? Let’s discover the language together by decompiling it to Java code. Today: the Kotlin Class!

Today: the Kotlin Class

Defining a class in Kotlin is fairly straight forward. Unlike Java, you can simply add a new class in any Kotlin file:

class Animal

Now, let’s just compile it and look at the decompiled Java code:

public final class Animal {}

Ok, that looks a bit silly, just a final class…​ Having some properties will do us good:

class Animal(name: String, dateOfBirth: LocalDate, legs: Int)

And the decompiled compiled code again:

public final class Animal {
   public Animal(@NotNull String name, int legs) {
      Intrinsics.checkNotNullParameter(name, "name");
      super();
   }
}

O boy, we still can’t do much with this a class; the name and legs fields do not even exist within the class itself. But we are on to something. The (name: String, …​) part actually just another way to write a constructor! If we want fields, getters and setters, we have to add the var keyword:

class Animal(var name: String, var dateOfBirth: LocalDate, var legs: Int)

Yeah, that looks more like it. It’s even uses @NotNull annotations where possible, which gives nice interoperability if you want to use Java and Kotlin together in one project:

public final class Animal {
   @NotNull
   private String name;
   private int legs;

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final void setName(@NotNull String name) {
      Intrinsics.checkNotNullParameter(name, "<set-?>");
      this.name = name;
   }

   @NotNull
   public final LocalDate getDateOfBirth() {
      return this.dateOfBirth;
   }

   public final void setDateOfBirth(@NotNull LocalDate dateOfBirth) {
      Intrinsics.checkNotNullParameter(dateOfBirth, "<set-?>");
      this.dateOfBirth = dateOfBirth;
   }

   public final int getLegs() {
      return this.legs;
   }

   public final void setLegs(int legs) {
      this.legs = legs;
   }

   public Animal(@NotNull String name, int legs) {
      Intrinsics.checkNotNullParameter(name, "name");
      super();
      this.name = name;
      this.legs = legs;
   }
}
You can also use the val keyword, which would remove the setters and make the class immutable.

Nice, but how can we add methods to the class? Well, it works exactly like Java code, just add a function to the class:

class Animal(var name: String, var dateOfBirth: LocalDate, var legs: Int) {
    fun getAge() = Period.between(dateOfBirth, LocalDate.now()).years
}

And the function is represented as method of the class:

public final class Animal {
   // fields

   public final int getAge() {
      Period period = Period.between(this.dateOfBirth, LocalDate.now());
      Intrinsics.checkNotNullExpressionValue(period, "Period.between(dateOfBirth, LocalDate.now())");
      return period.getYears();
   }

   // rest of implementation
}

But wait, come to think about it, the class has been made final automatically. So, we can’t extend the Animal class if we want to? Yes that’s right, by default in Kotlin classes are final, so they can’t be inherited. To make a class inheritable, mark it with the open keyword:

open class Animal(var name: String, var dateOfBirth: LocalDate, var legs: Int) {
   // same implementation
}

We can see it works in the reflected Java source code:

public class Animal {
   // same implementation
}

As the Animal class is open now, let’s extend it with a Dog class:

class Dog(name: String, dateOfBirth: LocalDate, legs: Int, val breed: String) : Animal(name, dateOfBirth, legs)

The syntax will probably look a little weird to you, so let’s explain in. If you think about the extends keyword abstractly, you could argue it states that the extended class is simply of the type it extends from. Kotlin uses the : operator to separate a name from a type, so for this case we express the Dog class is of type Animal. As the Animal class has a constructor, the compiler forces you to call it. In above example we just used the proper fields, that is name, dateOfBirth and legs, of the Dog class to call the constructor of the Animal class. We can also hard code some values if we want to:

class Dog(name: String, dateOfBirth: LocalDate, val breed: String) : Animal(name, dateOfBirth, legs = 4)

Pooh, with this knowledge, let’s discover the Java version:

public final class Dog extends Animal {
   @NotNull
   private final String breed;

   @NotNull
   public final String getBreed() {
      return this.breed;
   }

   public Dog(@NotNull String name, @NotNull LocalDate dateOfBirth, @NotNull String breed) {
      Intrinsics.checkNotNullParameter(name, "name");
      Intrinsics.checkNotNullParameter(dateOfBirth, "dateOfBirth");
      Intrinsics.checkNotNullParameter(breed, "breed");
      super(name, dateOfBirth, 4);
      this.breed = breed;
   }
}

Sweet! Last question, what about interfaces? Ah, nothing special about those. They act just like Java interfaces, the only difference being that there is also no implements keyword. What was true for extending classes is also true for interfaces. You simply tell the compiler that your class is of type X, Y and Z:

interface Noiseable {
    fun makeNoice()
}

class Dog(name: String, dateOfBirth: LocalDate, val breed: String) : Animal(name, dateOfBirth, legs = 4), Noiseable {
    override fun makeNoice() {
        println("Woof")
    }
}

So let’s check the Java code again:

public interface Noiseable {
   void makeNoice();
}

public final class Dog extends Animal implements Noiseable {
   // breed field

   public void makeNoice() {
      System.out.println("Woof");
   }

   // rest of implementation
}

Well, that’s enough for one day. Stay tuned for more!

shadow-left