Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

JCCONF 2017 - Tips for Kotlin Developers

Nevin
September 29, 2017

JCCONF 2017 - Tips for Kotlin Developers

Cost & Concern
Idioms
Standard Library
Kotlin is Great, but What's Next?

Nevin

September 29, 2017
Tweet

More Decks by Nevin

Other Decks in Technology

Transcript

  1. Hard • Method count [Use Proguard] • APK size •

    Before Proguard : 895 KB (JAR) / 1119 KB (DEX) (6317 methods). • After Proguard : • Build Time [Gradle-Profiler] • Static Analysis, • KLint, detekt • Android Lint in AS 3.0 • Build Options • Parallel 16838 -> 16924 380 KB for debug, 15 KB for release Incremental , Instant Run
  2. Soft • Learning Curve • Best Practice • Code Review

    • Hiring < 1W Kotlin Bytecode
 Convention Leader / Responsibility
  3. Hidden Cost [on ART] [on HotSpot] • Higher-order functions and

    Lambda expressions • Companion Object • Local Functions • Null safety • Indirect access to Range • Ranges (local, non-primitive types) • Ranges (iteration with explicit step) • Use of indices on custom collection • Varargs + Spread Operator • Delegate Properties (10%) • Ranges (forEach function)
  4. Property fun main(args: Array<String>) {
 println(Order().amount)
 }
 
 class Order

    {
 var amount = 0
 get() {
 if (amount > 100) {
 return amount
 } else {
 return 0
 }
 }
 }
  5. Backing Field fun main(args: Array<String>) {
 println(Order().amount)
 }
 
 class

    Order {
 var amount = 0
 get() {
 if (field > 100) {
 return amount
 } else {
 return 0
 }
 }
 }
  6. Object class Service { object Factory { fun create() =

    Service() } } fun test() { val service = Service.Factory.create() }
  7. Companion Object class Service { companion object Factory { fun

    create() = Service() } } fun test() { val service = Service.create() }
  8. Companion or not? • companion object must have a company

    :) • companion object can skip it’s name when called. • Each class can at most has one companion object.
  9. Package Level Function // File : Component.kt package com.example class

    Service fun create() = Service() // Use in kotlin in another package fun main(args: Array<String>) { var s: Service? = com.exmaple.create() } // Use in Java public static final void main(@NotNull String[] args) { Service s = com.exmaple.ComponentKt.create(); } each file will generate a separate class
  10. Factory class Service private constructor(){ private var endpoint: String =

    "dev.api.com" companion object { fun create() = Service().apply { endpoint = "prod.api.com" } } } private member access
  11. Companion Object class MyClass {
 
 companion object {
 private

    val TAG = "TAG"
 }
 
 fun helloWorld() {
 println(TAG)
 }
 }
  12. class MyClass {
 
 companion object {
 private val TAG

    = "TAG"
 }
 
 fun helloWorld() {
 println(TAG)
 }
 } public final class MyClass {
 
 public static final class Companion {
 private final String getTAG() {
 return MyClass.TAG;
 }
 
 private Companion() { }
 } private static final String TAG = "TAG";
 public static final MyClass.Companion Companion = new MyClass.Companion(….);
 
 public final void helloWorld() {
 String var1 = Companion.getTAG();
 System.out.println(var1);
 }
 }
  13. class MyClass {
 
 companion object {
 private val TAG

    = "TAG"
 }
 
 fun helloWorld() {
 println(TAG)
 }
 } public final class MyClass {
 
 public static final class Companion {
 private final String getTAG() {
 return MyClass.TAG;
 }
 
 private Companion() { }
 } private static final String TAG = "TAG";
 public static final MyClass.Companion Companion = new MyClass.Companion(….);
 
 public final void helloWorld() {
 String var1 = Companion.getTAG();
 System.out.println(var1);
 }
 } extra method call
  14. Companion Object class MyClass {
 
 companion object {
 const

    val TAG = "TAG"
 }
 
 fun helloWorld() {
 println(TAG)
 }
 } public final class MyClass {
 
 public static final class Companion {
 private final String getTAG() {
 return MyClass.TAG;
 }
 
 private Companion() { }
 } private static final String TAG = "TAG";
 public static final MyClass.Companion Companion = MyClass.Companion(….);
 
 public final void helloWorld() {
 String var1 = "TAG";
 System.out.println(var1);
 }
 }
  15. Lambda fun higher(op: () -> Unit) {
 println("start")
 op()
 println("end")


    }
 
 fun main(args: Array<String>) {
 higher({ println("HI") })
 } Let’s look at the client side
  16. Lambda - decompile public static final void higher(@NotNull Function0 op)

    {
 Intrinsics.checkParameterIsNotNull(op, "op");
 String var1 = "start";
 System.out.println(var1);
 op.invoke();
 var1 = "end";
 System.out.println(var1);
 }
 
 public static final void main(@NotNull String[] args) {
 Intrinsics.checkParameterIsNotNull(args, "args");
 higher((Function0)null.INSTANCE);
 } extra object extra method call
  17. Lambda inline fun higher(op: () -> Unit) {
 println("start")
 op()


    println("end")
 }
 
 fun main(args: Array<String>) {
 higher({ println("HI") })
 }
  18. Lambda - inline public static final void higher(@NotNull Function0 op)

    {
 Intrinsics.checkParameterIsNotNull(op, "op");
 String var2 = "start";
 System.out.println(var2);
 op.invoke();
 var2 = "end";
 System.out.println(var2);
 }
 
 public static final void main(@NotNull String[] args) {
 Intrinsics.checkParameterIsNotNull(args, "args");
 String var1 = "start";
 System.out.println(var1);
 String var2 = "HI";
 System.out.println(var2);
 var1 = "end";
 System.out.println(var1);
 } Copy Paste
  19. Reified Generics // Kotlin inline fun <reified T> isA(value: Any)

    = value is T fun main(args: Array<String>) { println(isA<String>("abc")) } // Java public static final void main(@NotNull String[] args) { Object value$iv = "abc"; boolean var3 = value$iv instanceof String; System.out.println(var3); }
  20. infix annotation infix fun String.shouldBeEqualTo(value: String) = this == value


    
 
 fun main(args: Array<String>) {
 
 val output = "Hello"
 
 output.shouldBeEqualTo("Hello")
 output shouldBeEqualTo "Hello"
 
 } more expressive
  21. Non-local Returns fun containingFunction() { val numbers = 1..10 numbers.forEach

    { print("$it ") if (it % 5 == 0) return } println("END") } 1 2 3 4 5
  22. Local Returns fun containingFunction4() { val numbers = 1..10 numbers.forEach

    myLabel@ { print("$it ") if (it % 5 == 0) return@myLabel } println("END") } 1 2 3 4 5 6 7 8 9 10 END
  23. Local Returns fun containingFunction4() { val numbers = 1..10 numbers.forEach

    { print("$it ") if (it % 5 == 0) return@foreach } println("END") } 1 2 3 4 5 6 7 8 9 10 END
  24. Local Returns fun containingFunction5() { val numbers = 1..10 numbers.forEach(

    fun(element) { print("$element ") if (element % 5 == 0) return }) println("END") } anonymous function 1 2 3 4 5 6 7 8 9 10 END
  25. Tail Recursion* 
 fun factorial(number: Int): Int {
 when (number)

    {
 0, 1 -> return 1
 else -> return number * factorial(number - 1)
 }
 }
 
 
 tailrec fun factorialTR(number: Int, accumulator: Int = 1): Int {
 when (number) {
 0 -> return accumulator
 else -> return factorialTR(number - 1, accumulator * number)
 }
 }
 
 
 fun main(args: Array<String>) {
 println(factorial(5))
 println(factorialTR(5))
 }
  26. Tail Recursion* public static final int factorial(int number) { switch(number)

    { case 0: case 1: return 1; default: return number * factorial(number - 1); } } public static final int factorialTR(int number, int accumulator) { while(true) { switch(number) { case 0: return accumulator; default: int var10000 = number - 1; accumulator *= number; number = var10000; } } }
  27. .. vs until // Kotlin for (i in 0..width -

    1) // Java byte width = 11; 
 int i = 0;
 int var2 = width - 1;
 if(i <= var2) {
 while(i != var2) {
 ++i;
 }
 }
 
 // Kotlin for (i in 0 until width) // Java byte width = 11;
 IntRange var10000 = RangesKt.until(0, width); int i = var10000.getFirst();
 int var2 = var10000.getLast();
 if(i <= var2) {
 while(i != var2) {
 ++i;
 }
 }

  28. Lazy Delegation class LazyProperty { val lazy: Int by lazy

    { println("Calculate the value") 42 } } fun main(args: Array<String>) { println("--- creation ---") val property = LazyProperty() println("--- first access ---${property.lazy}") println("--- second access ---${property.lazy}") } --- creation --- Calculate the value --- first access ---42 --- second access ---42
  29. Observable class Person(val name: String, a: Int, s: Int) {

    private val observer = { prop: KProperty<*>, old: Int, new: Int -> println("[${prop.name}] $old => $new") } var age by Delegates.observable(a, observer) var salary by Delegates.observable(s, observer) } fun main(args: Array<String>) { val person = Person("Alice", 25, 2000) person.age++ person.salary += 100 } [age] 25 => 26 [salary] 2000 => 2100
  30. Observable public class MyActivity : Activity() { private var state:

    UIState by Delegates.observable(UIState.SIGN_IN_PROMPT) { property, oldValue, newValue ->
 runOnUiThread {
 // Set UI state accordingly……
 when (newValue) {
 UIState.SIGN_IN_PROMPT -> {…}
 UIState.ERROR -> {…}
 UIState.LOADING -> {…}
 UIState.SHOW_DATA -> {…}
 }
 }
 } // state = uiState
  31. let val list: ArrayList<String>? = ArrayList() var result = list?.let

    { it.add("A") it.add("B") it.add("C") it.remove("A") it.removeAt(1) print(it) } [B]
  32. let val list: ArrayList<String>? = ArrayList() var result = list?.let

    { it.add("A") it.add("B") it.add("C") it.remove("A") it.removeAt(1) print(it) if (it.size == 0) "end" else true } print(result.toString()) [B] true
  33. apply val list: ArrayList<String>? = ArrayList() var result = list?.apply

    { add("A") add("B") add("C") remove("A") removeAt(1) print(this) } print("size=${result?.size}") [B] size=1 ArrayList<String>?
  34. Delegate class Service { var someProperty: String by ExternalFunctionality() }

    class ExternalFunctionality { var backingField = "Default" operator fun getValue(s: Service, p: KProperty<*>): String { println("===debug message====") return backingField } operator fun setValue(s: Service, p: KProperty<*>, v: String) { backingField = v } }
  35. Builder [type-safe-builder] val result = html { head { title

    { +"XML encoding with Kotlin" } } body { h1 { +"XML encoding with Kotlin" } a(href = “http://…”) { +"Kotlin" } } } println(result) <html> <head> <title> XML encoding with Kotlin </title> </head> <body> <h1 XML encoding with Kotlin </h1> <a href=“http://….”>Kotlin</a> </body> </html>
  36. Factory interface Factory<T> { fun create(): T } class A

    { private constructor() companion object : Factory<A> { override fun create(): A { return A() } } } fun main(args: Array<String>) { val a = A.create() }
  37. Tips • Beware of auto boxing 
 class Dog {


    var age = 3
 var weight: Int? = 5
 } public final class Dog {
 private int age = 3;
 @Nullable
 private Integer weight = Integer.valueOf(5);
 }

  38. Other tips • For Java library developers : make your

    SAM-type parameter the last element • Stream vs for loop : IO bound - Stream : CPU bound - for loop better
  39. Ktlint[blog] check.dependsOn ‘ktlint’ configurations {
 ktlint
 }
 
 dependencies {


    ktlint 'com.github.shyiko:ktlint:0.9.2'
 } task ktlint(type: JavaExec) {
 main = "com.github.shyiko.ktlint.Main"
 classpath = configurations.ktlint
 args "src/**/*.kt"
 } // $ ./gradlew ktlint run!
  40. Callback • new Task1().execute() {
 void onComplete() {
 new Task2().execute()

    {
 void onComplete() {
 new Task3().execute() {
 void onComplete() {
 // doWork()
 }
 }
 }
 }
 }
  41. Rx / Promise / Future • task1().then {
 task2()
 }.then()

    {
 task3()
 }.subscribe {
 updateUI()
 }
  42. Coroutine • launch(UI) {
 val r1 = task1()
 val r2

    = task2()
 val r3 = task3()
 updateUI(r1.await(), r2.await, r3.await())
 }
  43. async / await val time = measureTimeMillis { val one

    = async(CommonPool) { function1() } val two = async(CommonPool) { function2() } println("Result is ${one.await() + two.await()}") } println("Total time: $time")
  44. Thread fun threads() { val jobs = 1..100000 jobs.forEach {

    Thread() { Thread.sleep(1000L) print(".") }.start() } } Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:714)
  45. Coroutine fun coroutines() = runBlocking { val jobs = List(100000)

    { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } …………………………………………………………………………………………………………………
  46. FireFox Data • Sync with your History, Bookmarks, Password •

    Kotlin Ready! • Contribute
 on [github]
  47. Firefox For Android • Secure / Fast • Stay Tune

    :) • Contribute :
 with [git] on
 [mercurial]
  48. News & Resources • Coding Conventions • KotlinConf • Books

    : Kotlin in Action / Kotlin for Android Developers • Offcial Document • Podcast: Talking Kotlin • Kotlin Native