Share
Embed
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
Transcript
117 slides
-
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 -
Talk Not-Goals • Widest audience • 100% coverage • Deep
dives • Eggplant-measuring -
Kotlin • JVM language • Modern • Concise • Restrained
• Multi-paradigm (OO, FP) -
Java 7 final String firstGreeting = "Hello, world!"; Kotlin val
firstGreeting = "Hello, world!" -
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?" -
Java 7 public String getGreeting(final String name) { return "Hello,
" + name + "!"; } Kotlin fun getGreeting(name: String): String { return "Hello, " + name + "!" } -
Java 7 new Function<String, Integer>() { @Override public Integer apply(final
String input) { return input.length(); } }; -
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 -
Fun? • Expressive (of my ideas; for readers) • Concise
• Unsurprising • Forgiving • Ascendent • Supported -
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 -
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 -
Java 7 public final class PasswordUtil { public static boolean
isValidPassword(String candidate) { /* Logic */ } private PasswordUtil() { /* No instances */ } } if (PasswordUtil.isValidPassword("swordfish")) { /* Conditional logic */ } -
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 */ } -
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() } -
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) -
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 -
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 } } -
Java 7 final List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.get(0); // 1 list.get(list.size() - 1); // 4 -
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 -
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); } -
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) -
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); } } -
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) -
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; } } -
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 -
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; } } -
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 -
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 += "; "; } } -
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" -
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 -
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)" -
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)" -
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 -
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) -
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 -
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! -
Kotlin sealed class PaymentMethod { class Cash : PaymentMethod() class
Card(val number: String) : PaymentMethod() } -
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! } } -
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! } } -
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! } } -
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! } } -
Profit? • Null-safe • 100% interoperable with Java • Better
mutability support • Boilerplate reduction • Effective Java baked right in -
"I call it my billion-dollar mistake. It was the invention
of the null reference in 1965." -
"The trillion dollar mistake: assuming the billion dollar mistake of
null is its existence, and not the absence of support in the type system." -
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; } } -
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()); } } } -
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") } -
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}") -
Kotlin val l: Int = if (b != null) b.length
else -1 val l: Int = b?.length ?: -1 // Elvis operator ?: “Thank you very much” -
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 -
Kotlin val nullableList: List<Int?> = listOf(1, 2, null, 4) val
intList: List<Int> = nullableList.filterNotNull() // listOf(1, 2, 4) -
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 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 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? -
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) -
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 -
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"); -
Kotlin // example.kt package demo class Foo fun bar() {
// some function } // package-level function -
Kotlin // example.kt package demo class Foo fun bar() {
// some function } // package-level function Java 7 // Java new demo.Foo(); demo.ExampleKt.bar(); -
Kotlin // oldutils.kt @file:JvmName("Utils") @file:JvmMultifileClass package demo fun foo() {
} // newutils.kt @file:JvmName("Utils") @file:JvmMultifileClass package demo fun bar() { } -
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(); -
Kotlin class C(id: String) { @JvmField val ID = id
} Java 7 // Java class JavaClient { public String getID(C c) { return c.ID; } } -
Kotlin class C { companion object { @JvmStatic fun foo()
{} fun bar() {} } } -
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 -
Kotlin val items = listOf(1, 2, 3, 4) items.first() ==
1 items.last() == 4 items.filter { it % 2 == 0 } // returns [2, 4] -
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) -
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" -
Kotlin val numbers: MutableList<Int> = mutableListOf(1, 2, 3) val readOnlyView:
List<Int> = numbers -
Kotlin val numbers: MutableList<Int> = mutableListOf(1, 2, 3) val readOnlyView:
List<Int> = numbers println(numbers) // prints "[1, 2, 3]" -
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]" -
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 -
Kotlin val l = 1L + 3 // Long +
Int => Long, type of l inferred! -
Kotlin val c = Container(“Hello") // No new keyword required!
// Just invoke the constructor. // Also, how about that lack of ; ?!?! -
Ten consecutive clean builds with the Gradle daemon running (source:
https://medium.com/keepsafe-engineering/kotlin-vs-java-compilation-speed-e6c174b39b5d) -
Ten consecutive incremental builds with one core file changed (source:
https://medium.com/keepsafe-engineering/kotlin-vs-java-compilation-speed-e6c174b39b5d) -
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 -
class MyClass { companion object { private val TAG =
"TAG" } fun helloWorld() { println(TAG) } } /* A pretty innocent looking TAG in the singleton companion object... */ Companion Objects -
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 -
Investigate More Yourself! • Have Kotlin plugin installed in Android
Studio or IntelliJ • Tools -> Kotlin -> Show Kotlin Bytecode • Decompile button to see equivalent Java -
Resources: Learning • Koans (kotlinlang.org/docs/tutorials/) • Kotlin in Action (book)
• Exercism track (exercism.io/languages/kotlin) • #kotlin (Detroit Labs Slack) -
Resources: Community • Official Slack Channel (kotlinlang.slack.com) • "Kotlin Weekly"
Newsletter (kotlinweekly.net/) • "Talking Kotlin" Podcast (talkingkotlin.com/) • KotlinConf (2-3 Nov, San Francisco)