Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin For Fun And Profit (Video)

Kotlin For Fun And Profit (Video)

An introductory talk that aims to excite and empower Kotlin-curious Android developers. Focuses on improvements to both the developer experience (fun) and the end product (profit)!

Co-presented with my colleague, Matt Lauer.

Video: https://youtu.be/X5odzomCnfc
Venue: Detroit Labs

3a6060bc7ace07fa75791cd5dac2d46a?s=128

Stuart Kent

July 11, 2017
Tweet

Transcript

  1. Kotlin for Fun and Profit

  2. Talk Goals We want you to be: • Convinced of

    Kotlin's benefits • Aware of Kotlin's drawbacks • Excited to experiment with Kotlin • Empowered to start writing Kotlin in production
  3. Talk Not-Goals • Widest audience • 100% coverage • Deep

    dives • Eggplant-measuring
  4. Kotlin • JVM language • Modern • Concise • Restrained

    • Multi-paradigm (OO, FP)
  5. Milestones 2010 2016 2017 2015

  6. Java? & ❤

  7. Java? & ❤

  8. Basic Syntax

  9. Java 7 final String firstGreeting = "Hello, world!"; Kotlin val

    firstGreeting = "Hello, world!"
  10. Java 7 final String firstGreeting = "Hello, world!"; String secondGreeting

    = "Hello, world!"; secondGreeting = "How do you do, fellow kids?"; Kotlin val firstGreeting = "Hello, world!" var secondGreeting = "Hello, world!" secondGreeting = "How do you do, fellow kids?"
  11. Java 7 public String getGreeting(final String name) { return "Hello,

    " + name + "!"; } Kotlin fun getGreeting(name: String): String { return "Hello, " + name + "!" }
  12. Java 7 new Function<String, Integer>() { @Override public Integer apply(final

    String input) { return input.length(); } };
  13. Java 7 new Function<String, Integer>() { @Override public Integer apply(final

    String input) { return input.length(); } }; Kotlin { s: String -> s.length } // Has type (String) -> Int
  14. Fun

  15. Fun? • Expressive (of my ideas; for readers) • Concise

    • Unsurprising • Forgiving • Ascendent • Supported
  16. Extensions

  17. Java 7 public final class StringUtil { public static String

    capitalize(String string) { /* Logic */ } private StringUtil() { /* No instances */ } } final String capString = StringUtil.capitalize("raw string")); // Raw string
  18. Java 7 public final class StringUtil { public static String

    capitalize(String string) { /* Logic */ } private StringUtil() { /* No instances */ } } final String capString = StringUtil.capitalize("raw string")); // Raw string Kotlin fun String.capitalize() { /* Logic */ } val capString = "raw string".capitalize() // Raw string
  19. Java 7 public final class PasswordUtil { public static boolean

    isValidPassword(String candidate) { /* Logic */ } private PasswordUtil() { /* No instances */ } } if (PasswordUtil.isValidPassword("swordfish")) { /* Conditional logic */ }
  20. Java 7 public final class PasswordUtil { public static boolean

    isValidPassword(String candidate) { /* Logic */ } private PasswordUtil() { /* No instances */ } } if (PasswordUtil.isValidPassword("swordfish")) { /* Conditional logic */ } Kotlin fun String.isValidPassword() { /* Logic */ } if ("swordfish".isValidPassword()) { /* Conditional logic */ }
  21. Standard Library

  22. Kotlin fun String.substring(startIndex: Int, endIndex: Int): String { (this as

    java.lang.String).substring(startIndex, endIndex) } fun String.toUppercase(): String { (this as java.lang.String).toUppercase() }
  23. Kotlin fun String.substring(startIndex: Int, endIndex: Int): String { (this as

    java.lang.String).substring(startIndex, endIndex) } fun String.toUppercase(): String { (this as java.lang.String).toUppercase() } fun Char.isLowercase(): Boolean = Character.isLowercase(this)
  24. Kotlin fun String.substring(startIndex: Int, endIndex: Int): String { (this as

    java.lang.String).substring(startIndex, endIndex) } fun String.toUppercase(): String { (this as java.lang.String).toUppercase() } fun Char.isLowercase(): Boolean = Character.isLowercase(this) fun CharSequence.isNotEmpty(): Boolean = length > 0
  25. Kotlin fun String.substring(startIndex: Int, endIndex: Int): String { (this as

    java.lang.String).substring(startIndex, endIndex) } fun String.toUppercase(): String { (this as java.lang.String).toUppercase() } fun Char.isLowercase(): Boolean = Character.isLowercase(this) fun CharSequence.isNotEmpty(): Boolean = length > 0 fun String.capitalize(): String { return if (isNotEmpty() && this[0].isLowerCase()) { substring(0, 1).toUpperCase() + substring(1) } else { this } }
  26. Collection Operations

  27. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    list.get(0); // 1 list.get(list.size() - 1); // 4
  28. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    list.get(0); // 1 list.get(list.size() - 1); // 4 Kotlin val list = listOf(1, 2, 3, 4) list.first() // 1 list.last() // 4
  29. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    final List<Integer> transformedList = new ArrayList<>(); for (final Integer integer : list) { transformedList.add(integer * integer); }
  30. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    final List<Integer> transformedList = new ArrayList<>(); for (final Integer integer : list) { transformedList.add(integer * integer); } Kotlin val list = listOf(1, 2, 3, 4) val transformedList = list.map( { it * it } ) // listOf(1, 4, 9, 16)
  31. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    final List<Integer> filteredList = new ArrayList<>(); for (final Integer integer : list) { if (integer > 2) { filteredList.add(integer); } }
  32. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    final List<Integer> filteredList = new ArrayList<>(); for (final Integer integer : list) { if (integer > 2) { filteredList.add(integer); } } Kotlin val list = listOf(1, 2, 3, 4) val filteredList = list.filter( { it > 2 } ) // listOf(3, 4)
  33. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    boolean anyMatch = false; for (final Integer integer : list) { if (integer < 0) { anyMatch = true; break; } }
  34. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    boolean anyMatch = false; for (final Integer integer : list) { if (integer < 0) { anyMatch = true; break; } } Kotlin val list = listOf(1, 2, 3, 4) val anyMatch = list.any( { it < 0 } ) // false
  35. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    boolean allMatch = true; for (final Integer integer : list) { if (integer > 0) { allMatch = false; break; } }
  36. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    boolean allMatch = true; for (final Integer integer : list) { if (integer > 0) { allMatch = false; break; } } Kotlin val list = listOf(1, 2, 3, 4) val anyMatch = list.all( { it > 0 } ) // true
  37. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    String joinedList = ""; for (int index = 0; index > list.size(); index++) { joinedList += list.get(index); if (index < list.size() - 1) { joinedList += "; "; } }
  38. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    String joinedList = ""; for (int index = 0; index > list.size(); index++) { joinedList += list.get(index); if (index < list.size() - 1) { joinedList += "; "; } } Kotlin val list = listOf(1, 2, 3, 4) val joinedList = list.joinToString(separator = "; ") // "1; 2; 3; 4"
  39. Default Parameter Values

  40. Kotlin listOf(1, 2, 3, 4).joinToString(separator = "; ") // "1;

    2; 3; 4"
  41. Kotlin listOf(1, 2, 3, 4).joinToString(separator = "; ") // "1;

    2; 3; 4" fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String
  42. Kotlin listOf(1, 2, 3, 4).joinToString(separator = "; ") // "1;

    2; 3; 4" fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String listOf(1, 2, 3, 4).joinToString(prefix = "(", postfix = ")") // "(1, 2, 3, 4)"
  43. Kotlin Java 7 64 methods w/ collisions! listOf(1, 2, 3,

    4).joinToString(separator = "; ") // "1; 2; 3; 4" fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String listOf(1, 2, 3, 4).joinToString(prefix = "(", postfix = ")") // "(1, 2, 3, 4)"
  44. Data Classes

  45. data class Complex(val re: Double, val im: Double) Kotlin

  46. Kotlin data class Complex(val re: Double, val im: Double) val

    c1 = Complex(re = 1.0, im = 2.0) c1.re // Value: 1.0 c1.im // Value: 2.0
  47. Kotlin data class Complex(val re: Double, val im: Double) val

    c1 = Complex(re = 1.0, im = 2.0) c1.re // Value: 1.0 c1.im // Value: 2.0 println(c1) // Prints: Complex(re=1.0, im=2.0)
  48. Kotlin data class Complex(val re: Double, val im: Double) val

    c1 = Complex(re = 1.0, im = 2.0) c1.re // Value: 1.0 c1.im // Value: 2.0 println(c1) // Prints: Complex(re=1.0, im=2.0) val c2 = Complex(re = 1.0, im = 2.0) c1 == c2 // Value: true
  49. Kotlin data class Complex(val re: Double, val im: Double) val

    c1 = Complex(re = 1.0, im = 2.0) c1.re // Value: 1.0 c1.im // Value: 2.0 println(c1) // Prints: Complex(re=1.0, im=2.0) val c2 = Complex(re = 1.0, im = 2.0) c1 == c2 // Value: true Java 7 55 lines!
  50. Sealed Classes

  51. Kotlin sealed class PaymentMethod { class Cash : PaymentMethod() class

    Card(val number: String) : PaymentMethod() }
  52. Kotlin sealed class PaymentMethod { class Cash : PaymentMethod() class

    Card(val number: String) : PaymentMethod() } fun buildPaymentUrlSuffix(paymentMethod: PaymentMethod): String { return when (paymentMethod) { is PaymentMethod.Cash -> "/cash" is PaymentMethod.Card -> "/card/${paymentMethod.number}" // No default branch needed! } }
  53. Kotlin sealed class PaymentMethod { class Cash : PaymentMethod() class

    Card(val number: String) : PaymentMethod() } fun buildPaymentUrlSuffix(paymentMethod: PaymentMethod): String { return when (paymentMethod) { is PaymentMethod.Cash -> "/cash" is PaymentMethod.Card -> "/card/${paymentMethod.number}" // No default branch needed! } }
  54. Kotlin sealed class PaymentMethod { class Cash : PaymentMethod() class

    Card(val number: String) : PaymentMethod() } fun buildPaymentUrlSuffix(paymentMethod: PaymentMethod): String { return when (paymentMethod) { is PaymentMethod.Cash -> "/cash" is PaymentMethod.Card -> "/card/${paymentMethod.number}" // No default branch needed! } }
  55. Kotlin sealed class PaymentMethod { class Cash : PaymentMethod() class

    Card(val number: String) : PaymentMethod() } fun buildPaymentUrlSuffix(paymentMethod: PaymentMethod): String { return when (paymentMethod) { is PaymentMethod.Cash -> "/cash" is PaymentMethod.Card -> "/card/${paymentMethod.number}" // No default branch needed! } }
  56. Profit

  57. Profit? • Null-safe • 100% interoperable with Java • Better

    mutability support • Boilerplate reduction • Effective Java baked right in
  58. Profit: "Revenues"

  59. Null Safety

  60. "I call it my billion-dollar mistake. It was the invention

    of the null reference in 1965."
  61. "The trillion dollar mistake: assuming the billion dollar mistake of

    null is its existence, and not the absence of support in the type system."
  62. Java 7 public class ContainerWrapper { public Container container; public

    ContainerWrapper(Container container) { this.container = container; } } public class Container { public String string; public Container(String string) { this.string = string; } }
  63. Java 7 ContainerWrapper containerWrapper = Api.getContainerWrapper(); if (containerWrapper != null)

    { if (containerWrapper.container != null) { if (containerWrapper.container.string != null) { System.out.println("There are this many characters contained: " + containerWrapper.container.string.length()); } } }
  64. Java 7 ContainerWrapper containerWrapper = Api.getContainerWrapper(); if (containerWrapper != null)

    { if (containerWrapper.container != null) { if (containerWrapper.container.string != null) { System.out.println("There are this many characters contained: " + containerWrapper.container.string.length()); } } } Kotlin val containerWrapper = Api.getContainerWrapper() containerWrapper?.container?.string?.length.let { println("There are this many characters contained: $it") }
  65. Kotlin data class ContainerWrapper(val container: Container) data class Container(val string:

    String)
  66. Kotlin data class ContainerWrapper(val container: Container) data class Container(val string:

    String) val containerWrapper = Api.getContainerWrapper() println("There are this many characters contained: " + "${containerWrapper.container.string.length}")
  67. Kotlin val l: Int = if (b != null) b.length

    else -1
  68. Kotlin val l: Int = if (b != null) b.length

    else -1 val l: Int = b?.length ?: -1 // Elvis operator ?: “Thank you very much”
  69. Kotlin val l: Int = if (b != null) b.length

    else -1 val l: Int = b?.length ?: -1 // Elvis operator ?: “Thank you very much” val l = b!!.length // !! operator allows NPE
  70. Kotlin val nullableList: List<Int?> = listOf(1, 2, null, 4) val

    intList: List<Int> = nullableList.filterNotNull() // listOf(1, 2, 4)
  71. Java Interoperable

  72. Calling Java from Kotlin

  73. Java type Kotlin type byte kotlin.Byte short kotlin.Short int kotlin.Int

    long kotlin.Long char kotlin.Char float kotlin.Float double kotlin.Double boolean kotlin.Boolean
  74. Java type Kotlin type byte kotlin.Byte short kotlin.Short int kotlin.Int

    long kotlin.Long char kotlin.Char float kotlin.Float double kotlin.Double boolean kotlin.Boolean Java type Kotlin type java.lang.Object kotlin.Any! java.lang.Cloneable kotlin.Cloneable! java.lang.Comparable kotlin.Comparable! java.lang.Enum kotlin.Enum! java.lang.Annotation kotlin.Annotation! java.lang.Deprecated kotlin.Deprecated! java.lang.CharSequence kotlin.CharSequence! java.lang.String kotlin.String! java.lang.Number kotlin.Number! java.lang.Throwable kotlin.Throwable!
  75. Java type Kotlin type byte kotlin.Byte short kotlin.Short int kotlin.Int

    long kotlin.Long char kotlin.Char float kotlin.Float double kotlin.Double boolean kotlin.Boolean Java type Kotlin type java.lang.Object kotlin.Any! java.lang.Cloneable kotlin.Cloneable! java.lang.Comparable kotlin.Comparable! java.lang.Enum kotlin.Enum! java.lang.Annotation kotlin.Annotation! java.lang.Deprecated kotlin.Deprecated! java.lang.CharSequence kotlin.CharSequence! java.lang.String kotlin.String! java.lang.Number kotlin.Number! java.lang.Throwable kotlin.Throwable! Java type Kotlin type java.lang.Byte kotlin.Byte? java.lang.Short kotlin.Short? java.lang.Integer kotlin.Int? java.lang.Long kotlin.Long? java.lang.Character kotlin.Char? java.lang.Float kotlin.Float? java.lang.Double kotlin.Double? java.lang.Boolean kotlin.Boolean?
  76. Kotlin val list = ArrayList<String>() // non-null (constructor result) list.add("Item")

    val size = list.size // non-null (primitive int) val item = list[0] // platform type inferred (ordinary Java object)
  77. Kotlin val list = ArrayList<String>() // non-null (constructor result) list.add("Item")

    val size = list.size // non-null (primitive int) val item = list[0] // platform type inferred (ordinary Java object) item.substring(1) // allowed, may throw an exception if item == null
  78. Calling Kotlin from Java

  79. Kotlin var firstName: String = "Matt"

  80. Kotlin var firstName: String = "Matt" Java 7 private String

    firstName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } setFirstName("Matt");
  81. Kotlin // example.kt package demo class Foo fun bar() {

    // some function } // package-level function
  82. Kotlin // example.kt package demo class Foo fun bar() {

    // some function } // package-level function Java 7 // Java new demo.Foo(); demo.ExampleKt.bar();
  83. Kotlin // oldutils.kt @file:JvmName("Utils") @file:JvmMultifileClass package demo fun foo() {

    } // newutils.kt @file:JvmName("Utils") @file:JvmMultifileClass package demo fun bar() { }
  84. Kotlin // oldutils.kt @file:JvmName("Utils") @file:JvmMultifileClass package demo fun foo() {

    } // newutils.kt @file:JvmName("Utils") @file:JvmMultifileClass package demo fun bar() { } Java 7 // Java demo.Utils.foo(); demo.Utils.bar();
  85. Kotlin class C(id: String) { @JvmField val ID = id

    }
  86. Kotlin class C(id: String) { @JvmField val ID = id

    } Java 7 // Java class JavaClient { public String getID(C c) { return c.ID; } }
  87. Kotlin class C { companion object { @JvmStatic fun foo()

    {} fun bar() {} } }
  88. Kotlin class C { companion object { @JvmStatic fun foo()

    {} fun bar() {} } } Java 7 C.foo(); // works fine C.bar(); // error: not a static method C.Companion.foo(); // instance method remains C.Companion.bar(); // the only way it works
  89. Mutability Support

  90. Kotlin val items = listOf(1, 2, 3, 4)

  91. Kotlin val items = listOf(1, 2, 3, 4) items.first() ==

    1 items.last() == 4 items.filter { it % 2 == 0 } // returns [2, 4]
  92. Kotlin val items = listOf(1, 2, 3, 4) items.first() ==

    1 items.last() == 4 items.filter { it % 2 == 0 } // returns [2, 4] val rwList = mutableListOf(1, 2, 3)
  93. Kotlin val items = listOf(1, 2, 3, 4) items.first() ==

    1 items.last() == 4 items.filter { it % 2 == 0 } // returns [2, 4] val rwList = mutableListOf(1, 2, 3) rwList.requireNoNulls() // returns [1, 2, 3] if (rwList.none { it > 6 }) println("No items above 6") // prints "No items above 6"
  94. Kotlin val numbers: MutableList<Int> = mutableListOf(1, 2, 3) val readOnlyView:

    List<Int> = numbers
  95. Kotlin val numbers: MutableList<Int> = mutableListOf(1, 2, 3) val readOnlyView:

    List<Int> = numbers println(numbers) // prints "[1, 2, 3]"
  96. Kotlin val numbers: MutableList<Int> = mutableListOf(1, 2, 3) val readOnlyView:

    List<Int> = numbers println(numbers) // prints "[1, 2, 3]" numbers.add(4) println(readOnlyView) // prints "[1, 2, 3, 4]"
  97. Kotlin val numbers: MutableList<Int> = mutableListOf(1, 2, 3) val readOnlyView:

    List<Int> = numbers println(numbers) // prints "[1, 2, 3]" numbers.add(4) println(readOnlyView) // prints "[1, 2, 3, 4]" readOnlyView.clear() // -> does not compile
  98. Boilerplate Reduction

  99. Kotlin val l = 1L + 3 // Long +

    Int => Long, type of l inferred!
  100. Kotlin val c = Container(“Hello") // No new keyword required!

    // Just invoke the constructor. // Also, how about that lack of ; ?!?!
  101. Profit: "Costs"

  102. Performance Overhead

  103. Gradle Build Times

  104. Ten consecutive clean builds without the Gradle daemon (source: https://medium.com/keepsafe-engineering/kotlin-vs-java-compilation-speed-e6c174b39b5d)

  105. Ten consecutive clean builds with the Gradle daemon running (source:

    https://medium.com/keepsafe-engineering/kotlin-vs-java-compilation-speed-e6c174b39b5d)
  106. Ten consecutive incremental builds with one core file changed (source:

    https://medium.com/keepsafe-engineering/kotlin-vs-java-compilation-speed-e6c174b39b5d)
  107. Memory Overhead • More analysis on this at https://medium.com/ @BladeCoder/exploring-kotlins-hidden-costs-

    part-1-fbb9935d9b62
  108. var sum = 0 ints.filter { it > 0 }.forEach

    { sum += it } print(sum) /* Capturing lambdas (non-pure functions) like the above create a new Function object every time the lambda is passed into a higher-order function, then garbage collected. Unlike Java (requiring final for anonymous callbacks), variables captured in the closure can be modified. */ Functions
  109. class MyClass { companion object { private val TAG =

    "TAG" } fun helloWorld() { println(TAG) } } /* A pretty innocent looking TAG in the singleton companion object... */ Companion Objects
  110. public final class MyClass { private static final String TAG

    = "TAG"; public static final Companion companion = new Companion(); // synthetic public static final String access$getTAG$cp() { return TAG; } public static final class Companion { private final String getTAG() { return MyClass.access$getTAG$cp(); } // synthetic public static final String access$getTAG$p(Companion c) { return c.getTAG(); } } public final void helloWorld() { System.out.println(Companion.access$getTAG$p(companion)); } } Companion Objects
  111. Investigate More Yourself! • Have Kotlin plugin installed in Android

    Studio or IntelliJ • Tools -> Kotlin -> Show Kotlin Bytecode • Decompile button to see equivalent Java
  112. Some Setup Required

  113. The JVM

  114. Wrap-Up

  115. Resources: Learning • Koans (kotlinlang.org/docs/tutorials/) • Kotlin in Action (book)

    • Exercism track (exercism.io/languages/kotlin) • #kotlin (Detroit Labs Slack)
  116. Resources: Community • Official Slack Channel (kotlinlang.slack.com) • "Kotlin Weekly"

    Newsletter (kotlinweekly.net/) • "Talking Kotlin" Podcast (talkingkotlin.com/) • KotlinConf (2-3 Nov, San Francisco)
  117. Q&A