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

Stuart Kent

July 11, 2017
Tweet

More Decks by Stuart Kent

Other Decks in Technology

Transcript

  1. 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
  2. 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?"
  3. Java 7 public String getGreeting(final String name) { return "Hello,

    " + name + "!"; } Kotlin fun getGreeting(name: String): String { return "Hello, " + name + "!" }
  4. 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
  5. Fun

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

    • Unsurprising • Forgiving • Ascendent • Supported
  7. 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
  8. 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
  9. Java 7 public final class PasswordUtil { public static boolean

    isValidPassword(String candidate) { /* Logic */ } private PasswordUtil() { /* No instances */ } } if (PasswordUtil.isValidPassword("swordfish")) { /* Conditional logic */ }
  10. 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 */ }
  11. 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() }
  12. 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)
  13. 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
  14. 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 } }
  15. Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);

    list.get(0); // 1 list.get(list.size() - 1); // 4
  16. 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
  17. 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); }
  18. 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)
  19. 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); } }
  20. 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)
  21. 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; } }
  22. 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
  23. 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; } }
  24. 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
  25. 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 += "; "; } }
  26. 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"
  27. 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
  28. 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)"
  29. 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)"
  30. 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
  31. 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)
  32. 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
  33. 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!
  34. Kotlin sealed class PaymentMethod { class Cash : PaymentMethod() class

    Card(val number: String) : PaymentMethod() }
  35. 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! } }
  36. 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! } }
  37. 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! } }
  38. 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! } }
  39. Profit? • Null-safe • 100% interoperable with Java • Better

    mutability support • Boilerplate reduction • Effective Java baked right in
  40. "The trillion dollar mistake: assuming the billion dollar mistake of

    null is its existence, and not the absence of support in the type system."
  41. 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; } }
  42. 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()); } } }
  43. 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") }
  44. 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}")
  45. Kotlin val l: Int = if (b != null) b.length

    else -1 val l: Int = b?.length ?: -1 // Elvis operator ?: “Thank you very much”
  46. 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
  47. Kotlin val nullableList: List<Int?> = listOf(1, 2, null, 4) val

    intList: List<Int> = nullableList.filterNotNull() // listOf(1, 2, 4)
  48. 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
  49. 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!
  50. 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?
  51. 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)
  52. 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
  53. 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");
  54. Kotlin // example.kt package demo class Foo fun bar() {

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

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

    } // newutils.kt @file:JvmName("Utils") @file:JvmMultifileClass package demo fun bar() { }
  57. 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();
  58. Kotlin class C(id: String) { @JvmField val ID = id

    } Java 7 // Java class JavaClient { public String getID(C c) { return c.ID; } }
  59. 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
  60. Kotlin val items = listOf(1, 2, 3, 4) items.first() ==

    1 items.last() == 4 items.filter { it % 2 == 0 } // returns [2, 4]
  61. 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)
  62. 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"
  63. Kotlin val numbers: MutableList<Int> = mutableListOf(1, 2, 3) val readOnlyView:

    List<Int> = numbers println(numbers) // prints "[1, 2, 3]"
  64. 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]"
  65. 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
  66. Kotlin val l = 1L + 3 // Long +

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

    // Just invoke the constructor. // Also, how about that lack of ; ?!?!
  68. Ten consecutive clean builds with the Gradle daemon running (source:

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

    https://medium.com/keepsafe-engineering/kotlin-vs-java-compilation-speed-e6c174b39b5d)
  70. 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
  71. class MyClass { companion object { private val TAG =

    "TAG" } fun helloWorld() { println(TAG) } } /* A pretty innocent looking TAG in the singleton companion object... */ Companion Objects
  72. 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
  73. Investigate More Yourself! • Have Kotlin plugin installed in Android

    Studio or IntelliJ • Tools -> Kotlin -> Show Kotlin Bytecode • Decompile button to see equivalent Java
  74. Resources: Learning • Koans (kotlinlang.org/docs/tutorials/) • Kotlin in Action (book)

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

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