Adding Kotlin to existing Android project

Anton Rutkevich

JunoLab
GDG Minsk

About me

  • Use Java, Groovy, Kotlin
  • For Android, Google App Engine

Who's there? :)

  • What languages do your use?
  • What projects do you develop?

Why?
What?
How?

Why?

More code - more bugs

  • No lambdas (unless you use Java 8)
  • No properties; much boilerplate (Lombok)
  • String concatenation
  • *Utils classes

Easy to write non-safe code

  • non-final by default
  • nullable by default
  • No 'module' access  level
  • ...

What?

Main Kotlin goals

  • To create a Java-compatible language,    
  • That compiles at least as fast as Java,
  • Make it safer than Java
  • Make it more concise than Java
  • And make it way simpler than the most mature competitor – Scala.

Basics

Variables


/* Read-only */
val a: Int = 1

// `Int` type is inferred
val b = 1 

// Type required when no initializer is provided
val c: Int


/* Read-write */
var x = 5 // `Int` type is inferred
x += 1

Functions


fun sum(a: Int, b: Int): Int {
  return a + b
}

fun sum(a: Int, b: Int): Int = a + b

when

fun cases(obj: Any) {
    when (obj) {
        1          -> print("One")
        "Hello"    -> print("Greeting")
        is Long    -> print("Long")
        !is String -> print("Not a string")
        else       -> print("Unknown")
    }
}

Classes

Constructors

class Invoice

class Customer(val name: String)

class Dog(name: String) {
    init {
        logger.info("Initialized with ${name}")
    }
}

class MyView : View {
    constructor(ctx: Context) : super(ctx) {
    }

    constructor(ctx: Context, attrs: AttributeSet) 
       : super(ctx, attrs) {
    }
}

Inheritance

open class Base {
  open fun canBeOverridden() {}
  fun notOverrideable() {}
}

class Derived() : Base() {
  override fun canBeOverridden() {}
}

No statics

(companion objects instead)

class C {
  companion object {
    fun create() = C()
  }
}

fun main() {
  // C denotes the companion object here
  val c = C.create()

  // can be a value
  val cCompanionObject = C.Companion 
  val cObject: C = cCompanionObject.create();
}

Nested & Inner classes

class Outer {
  private val bar: Int = 1
  class Nested {
    fun foo() = 2
  }
}

class Outer {
  private val bar: Int = 1
  inner class Inner {
    fun foo() = bar
  }
}

Visibility modifiers

  • private — visible only in the declaring scope and its subscopes (inside the same module);
  • protected — (applicable only to class/trait members) like private, but also visible in subclasses;
  • internal — (used by default) visible everywhere within the same module (if the owner of declaring scope is visible);
  • public — visible everywhere (if the owner of declaring scope is visible).

Properties definition

var <propertyName>: <PropertyType> [= <property_initializer>]
  <getter>
  <setter>
public class Address { 
  public var name: String = ...
  
  var stringRepresentation: String
    get() = this.toString()
    set(value) {
      // parses string into this object
      setDataFromString(value) 
    }
}

Backing fields

// the initializer value is written 
// directly to the backing field
var counter = 0 
  set(value) {
    if (value >= 0)
      $counter = value
  }

// no backing field
val isEmpty: Boolean
  get() = this.size == 0

Backing properties

Null-safety

var a: String = "abc"
a = null // compilation error

var b: String? = "abc"
b = null // ok

val l = a.length() // valid
val m = b.length() // error: variable 'b' can be null

// safe casts
b?.length()

// Elvis!
val n = b?.length() ?: -1

Advanced

Extensions

(as *Utils alternative)

fun MutableList<Int>.swap(x: Int, y: Int) {
  // 'this' corresponds to the list
  val tmp = this[x] 
  this[x] = this[y]
  this[y] = tmp
}


val l = arrayListOf(1, 2, 3)

// 'this' inside 'swap()' will hold the value of 'l'
l.swap(0, 2) 

Resolved statically!

Lambdas

// function that takes lambda
fun synchronized(lock: Lock, body: () -> Unit): Unit {
  lock.lock()
  try {
    body()
  } finally {
    lock.unlock()
  }
}

// usage
synchronized (lock) {
  doSomething()
}

Lambdas 2

fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
  val result = ArrayList<R>()
  for (item in this)
    result.add(transform(item))
  return result
}

//usage
val ints = listOf(1, 2, 3)
val doubled = ints.map {it -> it * 2}

// also valid
val strings = listOf("one", "three", "five", "seven")
val transformed = strings filter {it.length == 5} 
                       sortBy {it} map {it.toUpperCase()}

Inline functions

// inline function
inline fun synchronized(lock: Lock, body: () -> Unit): Unit {
  ...
}

// the code ..
synchronized(lock) { foo() }

// .. will be transformed to
lock.lock()
try {
  foo()
} finally {
  lock.unlock()
}

Other cool features

  • Ranges
  • Type-safe builders
  • Delegation
  • Data classes
  • Multi-declarations
  • ...

Shut up and take my money method count*

Library Jar Size Method Count
kotlin-runtime-0.11.91.4 363 KB 952
kotlin-stdlib-0.11.91.4 656 KB 6142
scala-library-2.11.5 5.3 MB 50801
groovy-2.4.0-grooid 4.5 MB 29636
guava-18.0 2.2 MB 14833

* Based on Jake Wharton's research (see links), updated

How?

Install plugin

Add Kotlin to project

Convert Java to Kotlin :)

Or create new Kotlin files :)

Java Interop

  • Smooth in both directions
  • Provides type mapping
  • Nulls are the most dangerous part !

Links

Contacts

Adding Kotlin to existing Android project

By Anton Rutkevich

Adding Kotlin to existing Android project

  • 1,659