Skip to content
T.Czogalik-extern edited this page Apr 12, 2022 · 10 revisions

Kotlin

Literature

Head First Kotlin

Variables

var x = 5

  • Compiler creates Int object with value 5
  • Compiler assignes reference to Int object to variable x
  • compiler can infer variable type

Basic Types

Integers

  • Int, Byte, Short, Long
  • var x = 1 creates Int
  • var x = 1L creates Long
  • var x: Byte = 1 creates Byte

Floating points

  • Float, Double
  • var x = 1.0 creates Double
  • var x = 1.0F create Float

Boleans

var isBarking = true

Characters and String

  • var letter = 'c'
  • var name = "frodo"
  • String Templates
    • var x = 42; var value = "Value of x is $x"
    • use properties: var value ="my array has the size ${myArray.size}"

Arrays

  • var myArray = arrayOf(1,2,3,4)
  • each item contains reference to object
  • myArray[0]
  • myArray.size
  • compile infer type of array from its content
  • explicit array type:
    • var myArray:Array<Byte> = arrayOf(1,2,3)
  • var vs val: variable points to a different array vs the same array forever
    • still modify content using val array

Functions

Java void == Kotlin Unit

  • Unit is optional fun foo(arg:String):Unit
  • call by reference
  • can have default values
  • pass arguments by naming them
fun test(foo:String, bar: String = "") {}

test(foo = "foo", bar = "bar")

Loops

  • while(condition) {}
  • for (x in 1..10) {} // including 10
  • for (x in 1 until 10) {} // excluding 10
  • for (x in 10 downTo 1) {} // reverse order
  • for (x in 1..10 step 2) {} // increase step size
  • for (x in someArray) {} // loop through array elements

Classes

  • class Dog(val name: String){} // primary constructor
    • shortcut for
      class Dog(name_param: String){
        val name = name_param
      } 
      
  • mix is also possible
    class Dog(val name: String){
      val age = 10
    } 
    
  • initializer block are called when object is created. immediately after constructor was called
    class Dog(val name: String){
      init {
        println("Dog $name was created")
      }
    }
    
  • multiple initializer block are possible. Run in order of definition
  • custom getter
    class Dog(val weight: String){
      val weightInKg: Double
        get() = weight / 2.2
    }
    
    val dog = Dog(2.2)
    println(dog.weightInKg) // 1
    
  • custom setter
    class Dog(weightParam: String){
      var weight = weightParam
        set(value) {
          if (value > 0) field = value
        }
    }
    
    val dog = Dog(2.2)
    dog.weight = -1
    println(dog.weightInKg) // 2.2
    dog.weight = 10
    println(dog.weightInKg) // 10
    
  • secondary constructor
    • if class has primary constructor secondary constructor needs to delegate to primary
class Dog(name: String, weight: String) {
  constructor(weight_param:String) : this("Foo", weight_param) {}
}
  • constructor can have default values (like functions)
  • call constructor using named arguments (like functions)

Inheritance

  • explicitly mark classes and functions open for inheritance. otherwise subclasses cannot override member
  • ':' === Java extends
  • call super class primary constructor in subclass header
  • override members with override keyword
  • override val property with var (the other way is forbidden)
    • val property creates getter, if overriden with var, compiler adds a setter
  • override property type with subtype
  • class can inherit only one other class
open class Animal(val habitat:String) {
  open val food = ""
  open fun makeNoise() {}
}
class Hippo(habitat: String) : Animal(habitat) {
  override var food = ""
  override fun makeNoise() {}
}

abstract classes

  • cannot be instatiated
  • mark members with abstract keyword
    • abstract member do not need to be open
abstract class Animal {
  abstract val food: String
  abstract fun eat()
}

val animal = Animal() // compile error

interfaces

  • class can implement multiple interfaces
  • can have default implementation
  • can have properties
interface Roamable {
  fun roam() {
    ...
  }
}
class Animal : Roamable {
  override fun roam() {...}
}

data classes

  • == calls equals
    • checks object equivalence
  • === checks object identity
    • variables refer to same object
    • does not rely on equals method
  • Any is Javas Object
    • hashCode
    • toString
    • equals
  • data classes automaticaly override equals, hashCode, toString function to check equality based on values of objects properties
    • equals compares property values
    • classes considering equal return same hashCode
    • toString returns value for each property
    • If properties are defined in class body they are not considered in functions
  • object destructure
    • data classes define componentN functions
data class Recipe(val title: String = "", val isVegetarian: Boolean = false) {}

val r1 = Recipe("Chicken Bhuna")
val r2 = Recipe("Chicken Bhuna", false)
val r3 = Recipe(isVegetarian = true, title = "Thai Curry")

r1 == r2 // true
r1 === r2 // false
r1.equals(r2) // true
r1.hashCode().equals(r2.hashCode()) // true
r1.toString() // Recipe(title=Chicken Bhuna)

val title = r1.component1() // returns reference to first property in constructor
val isVegetarian = r1.component2()

val (title, isVegetatrian) = r1

Nulls

  • variables must explecitly declare that they are nullable
  • variables, parameters and return types can be nullable
var w : Wolf = null   // compile error
var w : Wolf? = null  // works

fun printInt(x:Int?) {}
fun result() : Int? {}
  • if you want to access member of nullable object compiler want's that you null check variable
  • safe call
    • member acces only if object is not null
    • expression return null if object is null
    • let block runs code if value is not null
      • it variable within let block refers to the null checked variable
  • elvis operator ?:
    • if left side is not null return it. Otherwise return rigth side
  • !! throws null pointer exception
val w: Wolf? = null
w.eat()     // compile error

// manualy null check
if (w != null) {
  w.eat()   // works
}

// safe call
w?.eat()

w?.let {
  println(it.hunger)
}
w?.hunger ?: -1 // if w is null -1 is returned
w!!.hunger      // if w is null throw null pointer exception

Exception

  • try catch like java
  • try and throw are expressions (unlike java)
// try expression
// result is either string as integer or null
val result = try {
  str.toInt()
} catch(e:Exception) {
  null
}

// throw expression
// if w not null assign h to w.hunger else throw Exception
val h = w?.hunger ?: throw AnimalException()

Collections

  • simple collections are immutable
  • if you want add / remove use MutableList/*Set/*Map

List

val shopping: List<String> = listOf("Tea", "Eggs")
val mutableShopping: List<String> = mutableListOf("Tea", "Eggs")

Set

  • uses hashcode to check if value already in set
    • first compiler uses === to check new value against already contained values
    • if false it uses == to check new value against already contained values
val friends: Set<String> = setOf("Adam", "Adam", "Sue") // ignores duplicates

val fmutableFiends: Set<String> = mutableSetOf("Adam", "Adam", "Sue")

Map

val r1 = Recipe("Chicken Soup")
val r2 = Recipe("Beef Soup")
val r3 = Recipe("Pork Soup")

val recipes: Map<String, Recipe> = mapOf("Recipe1", r1,"Recipe2", r2,"Recipe3", r3)

recipes.containsKey("Recipe1")  // true
recipes.containsValue(r1)       // true
recipes.getValue("Recipe1)      // r1

val mutableRecipes: Map<String, Recipe> = mutableMapOf("Recipe1", r1,"Recipe2", r2,"Recipe3", r3)

mutableRecipes.put("Recipe4", Recipe("Curry"))

Generics

  • restrict Generics to supertype
class Contest<T: Pet> { ... }
  • Generics can be uses outside of classe
fun <T: Pet> listPet(t: T): List<T> {...}
  • if you want to use generic subtype in place of generic supertype the generic type needs to be covariant using out
interface Retailer<T> { ... }

val petRetailer: Retailer<Pet> = CatRetailer() // compile error

// covariant version
interface Retailer<out T> { ... }

val petRetailer: Retailer<Pet> = CatRetailer() // works
  • if you want to use generic supertype in place of generic subtype the generic type needs to be contravariant using in
    • class can be constructor contravariant only
class Vet<T: Pet> {...}

val catContest = Contest<Cat>(Vet<Pet>()) // compile error

// contravariant version
class Vet<in T: Pet> {...}

val catContest = Contest<Cat>(Vet<Pet>()) // works

Lambdas

  • defined within {}
  • are anonymouse
  • can be assigned to variable
  • call invoke to execute lambda
  • if single parameter and compile can infer type you can use it to refer to parameter
  • Unit if lambda has no return value
  • lambdas can be passed to functions
    • functions having lambda parameters or return types are higher order functions
    • can be moved outside ()
  • can be return type
  • use typealias for assigning alternative name for existing type
    typealias Doubleconversion = (Double) -> Double
    
val add = {x: Int, y:Int -> x + y}
add.invoke(6,7)
// or
add(6,7)

val addFive : (Int)->Int = {x -> x + 5}
// compile can infer type of x
val addFive: (Int) -> Int = {it + 5}

JPA

Pitfalls

Clone this wiki locally