Slide 1

Slide 1 text

Kotlin for Fun and Profit

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

Talk Not-Goals • Widest audience • 100% coverage • Deep dives • Eggplant-measuring

Slide 4

Slide 4 text

Kotlin • JVM language • Modern • Concise • Restrained • Multi-paradigm (OO, FP)

Slide 5

Slide 5 text

Milestones 2010 2016 2017 2015

Slide 6

Slide 6 text

Java? & ❤

Slide 7

Slide 7 text

Java? & ❤

Slide 8

Slide 8 text

Basic Syntax

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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?"

Slide 11

Slide 11 text

Java 7 public String getGreeting(final String name) { return "Hello, " + name + "!"; } Kotlin fun getGreeting(name: String): String { return "Hello, " + name + "!" }

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Java 7 new Function() { @Override public Integer apply(final String input) { return input.length(); } }; Kotlin { s: String -> s.length } // Has type (String) -> Int

Slide 14

Slide 14 text

Fun

Slide 15

Slide 15 text

Fun? • Expressive (of my ideas; for readers) • Concise • Unsurprising • Forgiving • Ascendent • Supported

Slide 16

Slide 16 text

Extensions

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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 */ }

Slide 21

Slide 21 text

Standard Library

Slide 22

Slide 22 text

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() }

Slide 23

Slide 23 text

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)

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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 } }

Slide 26

Slide 26 text

Collection Operations

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Java 7 final List 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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Java 7 final List list = Arrays.asList(1, 2, 3, 4); final List 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)

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

Java 7 final List list = Arrays.asList(1, 2, 3, 4); final List 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)

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Java 7 final List 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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

Java 7 final List 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

Slide 37

Slide 37 text

Java 7 final List 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 += "; "; } }

Slide 38

Slide 38 text

Java 7 final List 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"

Slide 39

Slide 39 text

Default Parameter Values

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

Kotlin listOf(1, 2, 3, 4).joinToString(separator = "; ") // "1; 2; 3; 4" fun Iterable.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)"

Slide 43

Slide 43 text

Kotlin Java 7 64 methods w/ collisions! listOf(1, 2, 3, 4).joinToString(separator = "; ") // "1; 2; 3; 4" fun Iterable.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)"

Slide 44

Slide 44 text

Data Classes

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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)

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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!

Slide 50

Slide 50 text

Sealed Classes

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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! } }

Slide 53

Slide 53 text

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! } }

Slide 54

Slide 54 text

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! } }

Slide 55

Slide 55 text

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! } }

Slide 56

Slide 56 text

Profit

Slide 57

Slide 57 text

Profit? • Null-safe • 100% interoperable with Java • Better mutability support • Boilerplate reduction • Effective Java baked right in

Slide 58

Slide 58 text

Profit: "Revenues"

Slide 59

Slide 59 text

Null Safety

Slide 60

Slide 60 text

"I call it my billion-dollar mistake. It was the invention of the null reference in 1965."

Slide 61

Slide 61 text

"The trillion dollar mistake: assuming the billion dollar mistake of null is its existence, and not the absence of support in the type system."

Slide 62

Slide 62 text

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; } }

Slide 63

Slide 63 text

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()); } } }

Slide 64

Slide 64 text

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") }

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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}")

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

Kotlin val nullableList: List = listOf(1, 2, null, 4) val intList: List = nullableList.filterNotNull() // listOf(1, 2, 4)

Slide 71

Slide 71 text

Java Interoperable

Slide 72

Slide 72 text

Calling Java from Kotlin

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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!

Slide 75

Slide 75 text

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?

Slide 76

Slide 76 text

Kotlin val list = ArrayList() // 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)

Slide 77

Slide 77 text

Kotlin val list = ArrayList() // 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

Slide 78

Slide 78 text

Calling Kotlin from Java

Slide 79

Slide 79 text

Kotlin var firstName: String = "Matt"

Slide 80

Slide 80 text

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");

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

Kotlin // example.kt package demo class Foo fun bar() { // some function } // package-level function Java 7 // Java new demo.Foo(); demo.ExampleKt.bar();

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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();

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

Kotlin class C(id: String) { @JvmField val ID = id } Java 7 // Java class JavaClient { public String getID(C c) { return c.ID; } }

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

Mutability Support

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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)

Slide 93

Slide 93 text

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"

Slide 94

Slide 94 text

Kotlin val numbers: MutableList = mutableListOf(1, 2, 3) val readOnlyView: List = numbers

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

Kotlin val numbers: MutableList = mutableListOf(1, 2, 3) val readOnlyView: List = numbers println(numbers) // prints "[1, 2, 3]" numbers.add(4) println(readOnlyView) // prints "[1, 2, 3, 4]" readOnlyView.clear() // -> does not compile

Slide 98

Slide 98 text

Boilerplate Reduction

Slide 99

Slide 99 text

Kotlin val l = 1L + 3 // Long + Int => Long, type of l inferred!

Slide 100

Slide 100 text

Kotlin val c = Container(“Hello") // No new keyword required! // Just invoke the constructor. // Also, how about that lack of ; ?!?!

Slide 101

Slide 101 text

Profit: "Costs"

Slide 102

Slide 102 text

Performance Overhead

Slide 103

Slide 103 text

Gradle Build Times

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

Memory Overhead • More analysis on this at https://medium.com/ @BladeCoder/exploring-kotlins-hidden-costs- part-1-fbb9935d9b62

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

class MyClass { companion object { private val TAG = "TAG" } fun helloWorld() { println(TAG) } } /* A pretty innocent looking TAG in the singleton companion object... */ Companion Objects

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

Investigate More Yourself! • Have Kotlin plugin installed in Android Studio or IntelliJ • Tools -> Kotlin -> Show Kotlin Bytecode • Decompile button to see equivalent Java

Slide 112

Slide 112 text

Some Setup Required

Slide 113

Slide 113 text

The JVM

Slide 114

Slide 114 text

Wrap-Up

Slide 115

Slide 115 text

Resources: Learning • Koans (kotlinlang.org/docs/tutorials/) • Kotlin in Action (book) • Exercism track (exercism.io/languages/kotlin) • #kotlin (Detroit Labs Slack)

Slide 116

Slide 116 text

Resources: Community • Official Slack Channel (kotlinlang.slack.com) • "Kotlin Weekly" Newsletter (kotlinweekly.net/) • "Talking Kotlin" Podcast (talkingkotlin.com/) • KotlinConf (2-3 Nov, San Francisco)

Slide 117

Slide 117 text

Q&A