1 of 56

Kotlin, the Pragmatic

Language for Android

Mike Gouline

Android Developer

gouline.net • @mikestruct

2 of 56

Agenda

  • Background
  • What is Kotlin?
  • Perfect for Android
  • Performance and cost
  • Case study
  • Migration guide
  • Community adoption
  • Current issues
  • Future releases
  • Summary

3 of 56

Background

4 of 56

Background

  • Apple got a nice(r) new language
  • Android stuck with Java
  • Not fair!

5 of 56

Problems with Java

  • Missing modern features
    • Lambdas, properties, higher-order functions
  • Null safety
    • NullPointerException
  • Boilerplate code
  • Features specific to JDK (and Android API)

6 of 56

What is Kotlin?

7 of 56

What is Kotlin?

  • Named after island in St. Petersburg
  • Programming language
    • Based on the JVM
    • Compact and modern (“better Java”)
    • Open source
  • Created by JetBrains
    • Built into Android Studio and IntelliJ IDEA
    • Used by JetBrains internally

8 of 56

History

  • Project Kotlin unveiled in July 2011
  • Kotlin 1.0 released in February 2016
  • “Language of the Month” - Dr. Dobb’s Journal (01/2012)

9 of 56

Syntax

  • Types follow variable/function names
  • Functions start with fun keyword
  • Default constructor in class signature
  • Semicolons not required

class Foo(name: String) : Bar(name) {

override fun makeStuff(): Stuff {

return Stuff()

}

}

10 of 56

Null safety

var str1: String? = null

str1?.trim() // doesn't run

str1 = "Not null anymore"

str1?.trim() // does runs

str1!!.trim() // runs anyway

val str2: String = "I am not null"

str2.trim() // no need for "?."

String str1 = null;

str1.trim(); // runs and crashes

str1 = "Not null anymore";

str1.trim(); // runs

String str2 = "I am not null";

str2.trim(); // runs

KOTLIN

JAVA

11 of 56

Lambdas

fun evens(nums: List<Int>) = nums.filter { it % 2 == 0 }

public List<Integer> evens(List<Integer> nums) {

List<Integer> numsCopy = new ArrayList<>(nums);

Iterator<Integer> numsItr = numsCopy.listIterator();

while (numsItr.hasNext()) {

Integer num = numsItr.next();

if (num % 2 != 0) numsItr.remove();

}

return numsCopy;

}

KOTLIN

JAVA

12 of 56

Data classes

public static class Island {

private String mName;

public Island(String name) { mName = name; }

public String getName() { return mName; }

public void setName(String name) { mName = name; }

@Override public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

Island island = (Island) o;

return mName != null ? mName.equals(island.mName) : island.mName == null;

}

@Override public int hashCode() { return mName != null ? mName.hashCode() : 0; }

}

data class Island(var name: String)

KOTLIN

JAVA

13 of 56

Properties in Java code

// Java code

public class Circle {

private float mRadius;

public float getRadius() { return mRadius; }

public void setRadius(float radius) { mRadius = radius; }

}

// Kotlin code

val circle = Circle()

circle.radius = 1.5f // => circle.setRadius(1.5f)

println(circle.radius) // => circle.getRadius()

14 of 56

Sealed classes (algebraic data types)

// Arithmetic expression

sealed class Expr {

class Const(val number: Double) : Expr()

class Sum(val e1: Expr, val e2: Expr) : Expr()

object NotANumber : Expr()

}

fun eval(expr: Expr): Double = when (expr) {

is Expr.Const -> expr.number

is Expr.Sum -> eval(expr.e1) + eval(expr.e2)

Expr.NotANumber -> Double.NaN

}

15 of 56

Named/optional arguments

// Argument "stroke" is optional

fun circle(x: Int, y: Int, rad: Int, stroke: Int = 1) {

...

}

// Argument "rad" is named and "stroke" defaults to 1

circle(0, 0, rad = 5)

16 of 56

Extension functions

// Extension to String

fun String.encodeSpaces(): String {

return this.replace(" ", "_")

}

println("one two three".encodeSpaces()) // output: one_two_three

17 of 56

Perfect for Android

18 of 56

Perfect for Android

  • Android stuck with Java 6 or 7 (depending on API)
  • Complete interop with Java
  • Compact runtime
  • Do more with less code

19 of 56

Why not others?

  • Scala
    • Huge runtime
    • Lots of garbage collection
  • Groovy
    • Large runtime
    • Average tooling support
  • Ceylon
    • Not much support for Android

20 of 56

Android extensions

  • View binding (like Butter Knife)
  • No instance variables required
  • How?
    • Import synthetic layout
      • import kotlinx.android.synthetic.main.<layout>.*
    • Use view by ID
      • E.g. txt_status.text = "OK"
    • Under the hood: synthetic calls replaced by functions

21 of 56

Android extensions

import kotlinx.android.synthetic.main.activity_main.*

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

btn_go.setText(R.string.go)

btn_go.setOnClickListener { v ->

txt_status.text = "Done"

}

}

22 of 56

Nullability

  • Remember nullable types, e.g. String vs String?
  • Compatible with @NonNull and @Nullable annotations
    • @NonNullString
    • @NullableString?
  • Works with @Inject annotation
    • @Inject lateinit val foo: Foo
    • Non-nullable, even though not instantiated

23 of 56

Annotation processing

  • Supported via kapt
  • The only change in build.gradle:
    • apt "com.google.dagger:dagger-compiler:2.7"
    • kapt "com.google.dagger:dagger-compiler:2.7"

24 of 56

Performance and cost

25 of 56

Performance

  • Compiled to bytecode (like Java)
  • No impact on performance
  • Some Kotlin code faster
    • Lambdas that can be inlined
    • Built-in operations faster than DIY implementations

26 of 56

Build time

  • Used to be a problem (in early releases)
  • Much improved with incremental builds
  • Keepsafe benchmarked compilation speed Kotlin vs Java
    • Link - goo.gl/WPs1Gx
  • Configurations (Gradle daemon running):
    • Clean builds
    • Incremental build - isolated file change
    • Incremental build - core file change

27 of 56

Build time: clean

Courtesy of Keepsafe Engineering blog - goo.gl/WPs1Gx

28 of 56

Build time: incremental - isolated file change

Courtesy of Keepsafe Engineering blog - goo.gl/WPs1Gx

29 of 56

Build time: incremental - core file change

Courtesy of Keepsafe Engineering blog - goo.gl/WPs1Gx

30 of 56

Cost

  • Kotlin Standard Library (1.0.4)
    • 5,723 methods
    • JAR size: 757 KB
    • DEX size: 1,012 KB
  • For comparison:
    • Fresco (0.14.0) - 11,122 methods
    • Guava (19.0) - 15,076 methods
    • Google Play Service (5.0.77) - 20,298 methods

31 of 56

Case study

32 of 56

Case study

  • Production app
    • Safedome
  • Converted approx. 95% of the code to Kotlin
    • Kotlin 1.0.2 (early 2016)
  • Enabled ProGuard
  • Used Kotlin features (instead of straight conversion)

33 of 56

Method count

All methods

34 of 56

Method count

Kotlin methods

35 of 56

Lines of code

36 of 56

APK size

37 of 56

Migration guide

38 of 56

Migration guide

  • Simple process
    • Add Gradle dependencies (plugin, runtime, etc.)
    • Start writing .kt files instead of .java ones
  • No need to migrate everything at once
    • Kotlin classes can co-exist with Java ones
  • IntelliJ has a Java-to-Kotlin converter
    • Not perfect but good start
    • Works with pasted code

39 of 56

Migration fears

  • Difficulty training developers
  • Unsupported libraries

Were they founded?

40 of 56

Migration fears

  • Difficulty training developers
  • Unsupported libraries

Were they founded? No

41 of 56

Migration fears

  • Difficulty training developers
    • Plenty of documentation
    • Desire to ditch Java motivates
  • Unsupported libraries
    • Java libraries work just fine
    • Most current libraries have Kotlin support threads

42 of 56

Community adoption

43 of 56

Community adoption

  • Popular in the Android community
  • Some companies using Kotlin in production:
    • Basecamp
    • NBC News Digital
    • Hootsuite
    • Prezi

44 of 56

Contributions

  • Libraries
    • Spek, Wasabi, RxKotlin and many more
  • Documentation
    • Books, articles, tutorials
  • Other IDE support
    • Eclipse
    • NetBeans

45 of 56

Gradle support

  • Write scripts/plugins in Kotlin (since Gradle 3.0)
    • Note: Groovy not deprecated or removed… for now
  • Works with Android plugin (since 2.2)
  • Better IDE support and performance

46 of 56

Not just Android

  • Kotlin is not limited to Android
  • Just happens to be a good match
  • Other applications
    • Back end: Spring, Vert.x, etc.
    • Front end: JavaScript
    • Any other Java applications

47 of 56

Current issues

48 of 56

Current issues

  • Issue #1: Reflection
    • Requires kotlin-reflect import
    • Works fine if you need it
    • ...but it adds 8k methods!
  • Solution:
    • Write files requiring reflection in Java
    • Example: Realm models

49 of 56

Current issues

  • Issue #2: IntelliJ plugin stability
    • Plugin crashes sometimes
    • Doesn’t crash the whole IDE
  • Solution:
    • Not a major annoyance
    • Only happens when doing something dodgy

50 of 56

Future releases

51 of 56

Future releases

  • 1.0.x track
    • Bug fixes
    • Stability improvements
    • IDE support
  • 1.1.x track
    • New features
    • Breaking changes (potentially)

52 of 56

Kotlin EAP 1.1

  • Coroutines
  • Type aliases
  • Bound callable references
  • Local delegation properties & inline properties
  • Relaxed rules for sealed classes and data classes
  • Scripting
  • Java 7/8 support
  • JavaScript

53 of 56

Kotlin EAP 1.1 (relevant to Android)

  • Coroutines
  • Type aliases
  • Bound callable references
  • Local delegation properties & inline properties
  • Relaxed rules for sealed classes and data classes
  • Scripting
  • Java 7/8 support
  • JavaScript

54 of 56

Summary

55 of 56

Summary

  • Kotlin is a light, modern, compact language
  • Compatible with Android
  • No significant performance overhead
  • Allows for gradual migration
  • Becoming widely adopted
  • In active development
  • Ready for production

56 of 56

Thank you!

More Kotlin talks at YOW! Connected 2016:

  • “Anko - The Ultimate Ninja of Kotlin Libraries?”
    • Speaker: Kai Koenig

gouline.net • @mikestruct