Object Oriented Programming Intro

The purpose of this workshop is to understand Kotlin OOP part from Java Developer Perspective. We will see many examples on how easy it is to call java code from Kotlin thus knowledge about java libraries is still valuable and you don't need to re-learn everything from scratch.

Repo with exercises : https://github.com/PawelWlodarski/kotlin-workshops

Declaring Classes

In this part we will see :

  • How to declare a class in Kotlin
  • How to use java classes in Kotlin
  • Java/Kotlin Byte Code Comparison
  • Constructors in Kotlin
  • Final values and variables
  • Private state in classes

FILE : ClassesInKotlin.kt

First of all notice that you don't have class named ClassesInKotlin because in Kotlin you can declare functions as top level constructs.

fun main(args: Array<String>) {
...
}

Java classes can be used directly in Kotlin code. In this first example we will Explain each part of Kotlin declaration syntax.

val userJava: UserJava = UserJava("George", 20)

Main differences from java

  • type is on the right side of a variable name
  • type can be interferred by compiler
  • no semicolons
  • val is like final in java

Compilation - Simple Classes Comparison

Because in Kotlin you can put many public classes into one file so our single demo file generate multiple classes. Let's compare interface generated from java code with the Kotlin counterpart.

ls -al target/classes/lodz/jug/kotlin/starter/oop/

(***)
 UserJava.class
 UserKotlin.class

Compare amount of code between Java and Kotlin.

Kotlin Code :

class UserKotlin(val name: String, val age: Int) {
    override fun toString() = "KotlineUser($name, $age)" 
}

Java Code :

public class UserJava {
    private String name;
    private int age;

    public UserJava(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "UserJava{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Both generate similar byte code:

javap  target/classes/lodz/jug/kotlin/starter/oop/UserKotlin.class 
Compiled from "ClassesiNKotlin.kt"
public final class lodz.jug.kotlin.starter.oop.UserKotlin {
  public java.lang.String toString();
  public final java.lang.String getName();
  public final int getAge();
  public lodz.jug.kotlin.starter.oop.UserKotlin(java.lang.String, int);
}

and Java

javap  target/classes/lodz/jug/kotlin/starter/oop/UserJava.class 
Compiled from "UserJava.java"
public class lodz.jug.kotlin.starter.oop.UserJava {
  public lodz.jug.kotlin.starter.oop.UserJava(java.lang.String, int);
  public java.lang.String getName();
  public int getAge();
  public java.lang.String toString();
}

Constructors

Lets look at class definition once again

class UserKotlin(val name: String, val age: Int) {
    override fun toString() = "KotlineUser($name, $age)"
}

Primary constructor is defined next to the class name. If you want to define another constructor (although because of default parameters it may not be necessary that often) then you need to define another function marked as constructor

class UserTwoConstructors(val name: String, val city: String) {
    constructor(name: String) : this(name, "LODZ") {
        Displayer.section("Use with two cons created with ($name,Lodz)") //no access to city here
    }
}

But what if you want to perform some operations in constructor ? For now it looks like you can only declare fields there however you have another construction to achieve this - init

class UserWithInit(val name: String, val city: String) {
    val other: String

    constructor(name: String) : this(name, "LODZ") {
        println("in constructor")
    }

    init {
        other = "otherValue"
        println("in init")
    }
}

standard javap will not tell us much here

javap  target/classes/lodz/jug/kotlin/starter/oop/UserWithInit.class 
Compiled from "ClassesiNKotlin.kt"
public final class lodz.jug.kotlin.starter.oop.UserWithInit {
  public final java.lang.String getOther();
  public final java.lang.String getName();
  public final java.lang.String getCity();
  public lodz.jug.kotlin.starter.oop.UserWithInit(java.lang.String, java.lang.String);
  public lodz.jug.kotlin.starter.oop.UserWithInit(java.lang.String);
}

but if we go deeper

pawel@maszyna:~/projects/workshops/kotlin$ javap -c  target/classes/lodz/jug/kotlin/starter/oop/UserWithInit.class 
Compiled from "ClassesiNKotlin.kt"

(...)
  public lodz.jug.kotlin.starter.oop.UserWithInit(java.lang.String, java.lang.String);
    Code:
       0: aload_1
       1: ldc           #24                 // String name
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_2
       7: ldc           #31                 // String city
       9: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_0
      13: invokespecial #34                 // Method java/lang/Object."<init>":()V
      16: aload_0
      17: aload_1
      18: putfield      #17                 // Field name:Ljava/lang/String;
      21: aload_0
      22: aload_2
      23: putfield      #21                 // Field city:Ljava/lang/String;
      26: aload_0
      27: ldc           #36                 // String otherValue
      29: putfield      #11                 // Field other:Ljava/lang/String;
      32: ldc           #38                 // String in init
      34: astore_3
      35: getstatic     #44                 // Field java/lang/System.out:Ljava/io/PrintStream;
      38: aload_3
      39: invokevirtual #50                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      42: return
(...)

Now we can see that everything from init is part of primary constructor.

Adding setters

You can choose accessibility level by manipulating val and var .

  • val - only getter
  • var - getter and setter
  • nothing - no getter or setter, just a parameter in the constructor
  • private val, private var - no getter neither setter but here we have field generated in the class body

so from class

class SocialMediaAccount(val name: String, var wall: String, friends: List<String>) {
    private var messages: List<String> = java.util.LinkedList()

    //property example - feature unique for Kotlin
    // show what happen when messages.size is asigned directly to field
    val numberOfMessage:Int
        get() = messages.size


    //can not use  '=' syntax - not expression
    fun addMessage(m: String) {
        messages += m
    }

    //remove type and show warning
    fun getMessages(): List<String> = Collections.unmodifiableList(messages)
}

We have generated bytecode.

javap   target/classes/lodz/jug/kotlin/starter/oop/SocialMediaAccount.class 
Compiled from "ClassesInKotlin.kt"
public final class lodz.jug.kotlin.starter.oop.SocialMediaAccount {
  public final int getNumberOfMessage();
  public final void addMessage(java.lang.String);
  public final java.util.List<java.lang.String> getMessages();
  public final java.lang.String getName();
  public final java.lang.String getWall();
  public final void setWall(java.lang.String);
  public lodz.jug.kotlin.starter.oop.SocialMediaAccount(java.lang.String, java.lang.String, java.util.List<java.lang.String>);

State encapsulation

Just put private in front of val/var/def.

class SocialMediaAccount(val name: String, var wall: String, friends: List<String>) {
    private var messages: List<String> = java.util.LinkedList()

    //can not use  '=' syntax - not expression
    fun addMessage(m: String) {
        messages += m
    }

    //remove type and show warning
    fun getMessages(): List<String> = Collections.unmodifiableList(messages)

}

Messages field is not accessible from outside the class . We have public function for adding messages and also public function for retrieving messages. This is standard approach known from java.

Property fields

Concept of Property fields may be not quite intuitive for java programmers at the beginning (but maybe it is - you never know).

class SocialMediaAccount(val name: String, var wall: String, friends: List<String>) {
    private var messages: List<String> = java.util.LinkedList()

    //property example - feature unique for Kotlin
    // show what happen when messages.size is assigned directly to field
    val numberOfMessage:Int
        get() = messages.size

(...)
 Displayer.section("Stevens account size property", account.numberOfMessage)

Notice that get method is connected with property by just being declared next to it. What is gain of property declaration ?

Now you can use syntax which looks like usage of standard field

 Displayer.section("Stevens account size property", account.numberOfMessage)

In bytecode we see that in result we have a standard method which uses private field messages

javap -c   target/classes/lodz/jug/kotlin/starter/oop/SocialMediaAccount.class 
Compiled from "ClassesInKotlin.kt"
public final class lodz.jug.kotlin.starter.oop.SocialMediaAccount {
  public final int getNumberOfMessage();
    Code:
       0: aload_0
       1: getfield      #11                 // Field messages:Ljava/util/List;
       4: invokeinterface #16,  1           // InterfaceMethod java/util/List.size:()I
       9: ireturn

Objects and Interfaces

In this part we will see:

  • Kotlin classes implementing Java interfaces
  • Calling java static methods from Kotlin
  • Kotlin Object concept

FILE : ObjectsAndInterfaces.kt

When switching to another language you may be concerned that your previous experience gained with Java will be wasted. This is not the case with Kotlin because Kotlin and Java has very nice interoperability. First of all you can easily add Kotlin implementation of a Java interface.

So if we have good old Java interface.

public interface JavaInterface {
    String toJson();
}

Kotlin implementation is really straightforward

class KotlinImplementation(private val value:String) : JavaInterface{

    override fun toJson(): String =
        """{
       | "value" : '$value'
       |}"""


}

And usage :

 val kotlinImplementation:JavaInterface=KotlinImplementation("This is Kotlin")
 Displayer.section("kotlin class with java interface",kotlinImplementation.toJson())

Java Static Methods

Also there is no problem with calling java static methods. When we have classic utility class :

public class StaticMethods {

    public final static String prefix(String prefix, String base){
        return prefix+"_"+base;
    }

    public final static Integer multiply(Integer i1, Integer i2){
        return i1*i2;
    }

}

then using it in the code is really straighforward

Displayer.title("calling java static methods from Kotlin")
Displayer.section("STATIC FROM JAVA 1 : "+StaticMethods.prefix("scala","printing"))
Displayer.section("STATIC FROM JAVA 2 (2*3): "+StaticMethods.multiply(2,3))

Interfaces in Kotlin

If we want simple interface without default methods like in Java 7 then we just need to declare empty methods with fun . Kotlin doesn't have distinction between extending classes and implementing interfaces you just need to put colon and type interface name

interface KotlinInterface {
    fun toJson():String
}


class SecondImplementation(val value:String) : KotlinInterface{
    override fun toJson(): String = """{"secondValue" : '$value'}"""
}

No surprises in byte code here. Kotlin interface was converted into standard Java interface and Kotlin implementation become standard Java class.

javap -c   target/classes/lodz/jug/kotlin/starter/oop/KotlinInterface.class 
Compiled from "ObjectsAndInterfaces.kt"
public interface lodz.jug.kotlin.starter.oop.KotlinInterface {
  public abstract java.lang.String toJson();
}


javap  target/classes/lodz/jug/kotlin/starter/oop/KotlinInterface.class 
Compiled from "ObjectsAndInterfaces.kt"
public interface lodz.jug.kotlin.starter.oop.KotlinInterface {
  public abstract java.lang.String toJson();
}

Objects

Time for a construction which you will not find in Java. You can easily call static java methods from Kotlin code yet Kotlin doesn't have the static keyword itself. To achieve similar behaviour we can create singleton object.

object FactoryExample{
    fun factoryMethod(v:String):KotlinInterface = SecondImplementation(v)
    infix fun infixFactory(v:String):KotlinInterface = SecondImplementation(v)
}

There is always only one instance of this object and it is a very good place to define factory methods for general class instances.

 javap  target/classes/lodz/jug/kotlin/starter/oop/FactoryExample.class 
Compiled from "ObjectsAndInterfaces.kt"
public final class lodz.jug.kotlin.starter.oop.FactoryExample {
  public static final lodz.jug.kotlin.starter.oop.FactoryExample INSTANCE;
  public final lodz.jug.kotlin.starter.oop.KotlinInterface factoryMethod(java.lang.String);
  public final lodz.jug.kotlin.starter.oop.KotlinInterface infixFactory(java.lang.String);
  static {};
}

infix - in short - if you want to write method invocation without dot and parentheses then this is for you.

infix fun infixFactory(v:String):KotlinInterface = SecondImplementation(v)
(...)
val factoredInfix: KotlinInterface = FactoryExample infixFactory "infixExample"

invoke - if you want to call methods without actually using their names (very useful for functions to not have f.apply(1) like in java)

operator fun invoke(v:String):KotlinInterface = SecondImplementation(v)
(...)
  val factoredInvoke=FactoryExample("invoke Example")
    Displayer.section("invoke",factoredInvoke.toJson())

Properties in Interfaces

You can declare properties also in interfaces. They can use other abstract methods so this will work similarly to template method

interface KotlinInterfaceWithPreoperty {
    fun toJson():String

    val trimmedJson:String
        get() = toJson().trim()
}

and usage

 Displayer.title("Properties in interfaces")
 val fourthImplementation=FourthImplementation("4th value")
 Displayer.section("4th toJson",fourthImplementation.toJson())
 Displayer.section("4th toJsonTrim",fourthImplementation.trimmedJson)

Kotlin Test

FILE : StaticMethodsTest

Kotlin has very nice testing framework inspired by scala test.

 <dependency>
            <groupId>io.kotlintest</groupId>
            <artifactId>kotlintest</artifactId>
            <version>{version}</version>
        </dependency>

It is a great ilustration of many concepts trained during those workshops.

You declare a test by extending one of couple "Specs" - let's start with StringSpec.

class StaticMethodsTest : StringSpec() {
    init {
        "Static Methods should add prefix " {
            val result = StaticMethods.prefix("prefix", "test")
            result shouldBe "prefix_test"
        }

        "Static Methods should add prefix " {
            val result = StaticMethods.multiply(4, 5)

            result shouldBe 20 //change to 19 and check result
        }
    }
}

First of all you need to put tests in init - so technically this is primary constructor.

No why following part even compiles ?

"Static Methods should add prefix " {

This is topic for the future but generally Kotlin allows you to override limited set of operators. Here we are overriding invoke operator and also we are using some syntax sugar from kotlin which allows you to pass lambdas with curly braces

 operator fun String.invoke(test: () -> Unit): TestCase {

and this :

result shouldBe "prefix_test"

here we see another two Kotlin features

  • extension method
  • infix operator which we saw already
infix fun String.shouldBe(other: String) {
  if (this != other) {
    throw ComparisonFailure("", other, this)
  }
}

So as you see everything here is a Kotlin native code.

Exercises

1) Test Java class

FILE : TestingJavaClassesExercise

  • replace ??? with valid Kotlin instructions to make code compile and test passing

  • when first test is ready uncomment second one

2) Statefull Instance in Kotlin

FILE : StatefulInstanceInKotlinExercise

  • Implement similar logic in Kotlin as it was in previous exercise in Java - class with general state. Implement trait HasState which works like a standard interface.

  • In the second part try to save changes history - this one is HARD when you are beginner but still worth to try. History will be kept in standard java.util.Collection. Here you will have to declare java List with Kotlin alias type MutableList

3) Factory Method

FILE : FactoryMethodExercise

You have provided an interface for processing strings:

interface Processor{
    fun process(input:String):String
}
  • implement two processors : one changes string to upper and seconds trim whitespaces

  • implement object factory which returns processor according to string id -declare id's according to tests

results matching ""

    No results matching ""