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

Kotlin, beyond the basics

Kotlin, beyond the basics

Talk given at 360AnDev (https://360andev.com/) 2018 in Denver Colorado, USA.

Video: https://www.youtube.com/watch?v=Ot2DOKztu38

Features 3 important topics for intermediate Kotlin developers - adapting to the functional programming paradigm, learning more about functions and lambdas, and learning about Kotlin-Java interoperability

Segun Famisa

July 20, 2018
Tweet

More Decks by Segun Famisa

Other Decks in Programming

Transcript

  1. Kotlin,
    beyond the basics

    View Slide

  2. segunfamisa
    segunfamisa.com

    View Slide

  3. Intro

    View Slide

  4. Imperative vs Functional Programming

    View Slide

  5. Imperative vs Functional Programming
    Functions in Kotlin - lambdas, stdlib functions, etc

    View Slide

  6. Imperative vs Functional Programming
    Functions in Kotlin - lambdas, stdlib functions, etc
    Java & Kotlin Interop

    View Slide

  7. Imperative vs Functional
    programming

    View Slide

  8. Functional == actions are composed into
    functions and usually reads as “what is done”
    Imperative == structured in steps of execution
    and usually reads as “how it’s done”
    Imperative == structured in steps of execution
    and usually reads as “how it’s done”

    View Slide

  9. Functional == actions are composed into
    functions and usually reads as “what is done”
    Imperative == structured in steps of execution
    and usually reads as “how it’s done”
    Functional == actions are composed into
    functions and usually reads as “what is done”
    Imperative == structured in steps of execution
    and usually reads as “how it’s done”

    View Slide

  10. // Java - print even numbers
    for (int i = start; i < end; i++) {
    if (i % 2 == 0) {
    System.out.println(i);
    }
    }

    View Slide

  11. // Java - print even numbers
    for (int i = start; i < end; i++) {
    if (i % 2 == 0) {
    System.out.println(i);
    }
    }
    // Kotlin - print even numbers
    (start until end)
    .filter { it % 2 == 0 }
    .map { println(it) }

    View Slide

  12. Imperative Functional

    View Slide

  13. Imperative Functional
    Immutability isn’t
    encouraged at language
    level
    Encourages immutability

    View Slide

  14. Imperative Functional
    Immutability isn’t
    encouraged at language
    level
    Encourages immutability
    Instances of classes,
    structures, objects are
    first class citizens
    Functions are first class
    citizens

    View Slide

  15. Imperative Functional
    Immutability isn’t
    encouraged at language
    level
    Encourages immutability
    Instances of classes,
    structures, objects are
    first class citizens
    Functions are first class
    citizens
    Loops, method calls,
    conditionals
    Function calls

    View Slide

  16. Imperative Functional
    Immutability isn’t
    encouraged at language
    level
    Encourages immutability
    Instances of classes,
    structures, objects are
    first class citizens
    Functions are first class
    citizens
    Loops, method calls,
    conditionals
    Function calls
    Side-effects allowed in
    functions
    Pure functions

    View Slide

  17. functions in Kotlin

    View Slide

  18. Functions are first class citizens

    View Slide

  19. Functions are first class citizens
    They can:
    ● be stored in a variable - just like other types

    View Slide

  20. Functions are first class citizens
    They can:
    ● be stored in a variable - just like other types
    ● take another function as parameter

    View Slide

  21. Functions are first class citizens
    They can:
    ● be stored in a variable - just like other types
    ● take another function as parameter
    ● return a function

    View Slide

  22. Functions are first class citizens
    They can:
    ● be stored in a variable - just like other types
    ● take another function as parameter
    ● return a function
    a.k.a higher order functions

    View Slide

  23. Lambdas & higher order
    functions

    View Slide

  24. Lambdas & higher order functions
    fun atLeastAndroidP(action: () -> Unit) {
    ...
    }

    View Slide

  25. Lambdas & higher order functions
    fun atLeastAndroidP(action: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    action()
    }
    }

    View Slide

  26. Lambdas & higher order functions
    // Usage:
    atLeastAndroidP {
    // do something on Android P
    }
    fun atLeastAndroidP(action: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    action()
    }
    }

    View Slide

  27. Lambdas & higher order functions
    fun atLeastAndroidP(action: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    action()
    }
    }
    Lambda
    // Usage:
    atLeastAndroidP {
    // do something on Android P
    }

    View Slide

  28. Lambdas & higher order functions
    fun atLeastAndroidP(action: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    action()
    }
    }
    Higher order function
    // Usage:
    atLeastAndroidP {
    // do something on Android P
    }

    View Slide

  29. Lambdas & higher order functions
    // Usage:
    atLeastAndroidP {
    // do something on Android P
    }
    fun atLeastAndroidP(action: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    action()
    }
    }

    View Slide

  30. Lambdas & higher order functions
    // Usage:
    atLeastAndroidP {
    // do something on Android P
    }
    fun atLeastAndroidP(action: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    action()
    }
    } Tip: Take a look at the generated
    code to understand what’s happening
    under the hood

    View Slide

  31. Lambdas & higher order functions
    // Generated Java code
    public final void atLeastAndroidP(@NotNull Function0 action) {
    if (VERSION.SDK_INT > 28) {
    action.invoke();
    }
    }
    fun atLeastAndroidP(action: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    action()
    }
    }

    View Slide

  32. Lambdas & higher order functions
    // Generated Java code
    public final void atLeastAndroidP(@NotNull Function0 action) {
    if (VERSION.SDK_INT > 28) {
    action.invoke();
    }
    }
    Every lambda is an object!
    Not great for performance
    fun atLeastAndroidP(action: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    action()
    }
    }

    View Slide

  33. () -> Unit Function0
    (Int) -> Boolean Function1
    (Int, String) -> Boolean Function2
    Every lambda corresponds to a FunctionN interface -
    where N is the number of params in the lambda

    View Slide

  34. Lambdas & higher order functions
    // FunctionN interfaces used by Kotlin
    interface Function0 : Function
    interface Function1 : Function
    interface Function2 : Function
    ...
    interface Function22 : Function {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2...p22: P22): R
    }

    View Slide

  35. Improving lambdas’ performance - the inline keyword
    // Kotlin
    fun atLeastAndroidP(action: () -> Unit) {...}

    View Slide

  36. Improving lambdas’ performance - the inline keyword
    // Kotlin
    inline fun atLeastAndroidP(action: () -> Unit) {...}

    View Slide

  37. Improving lambdas’ performance - the inline keyword
    // Kotlin
    inline fun atLeastAndroidP(action: () -> Unit) {...}
    // Usage:
    fun doSomethingOnP() {
    atLeastAndroidP { println("I'm on Android P") }
    }

    View Slide

  38. Improving lambdas’ performance - the inline keyword
    // Kotlin
    inline fun atLeastAndroidP(action: () -> Unit) {...}
    // Usage:
    fun doSomethingOnP() {
    atLeastAndroidP { println("I'm on Android P") }
    }
    // Generated Java code
    public final void doSomethingOnP() {
    if (VERSION.SDK_INT > 28) {
    String var0 = "I'm on Android P";
    System.out.println(var0);
    }
    }

    View Slide

  39. Improving lambdas’ performance - the inline keyword
    // Kotlin
    inline fun atLeastAndroidP(action: () -> Unit) {...}
    // Usage:
    fun doSomethingOnP() {
    atLeastAndroidP { println("I'm on Android P") }
    }
    // Generated Java code
    public final void doSomethingOnP() {
    if (VERSION.SDK_INT > 28) {
    String var0 = "I'm on Android P";
    System.out.println(var0);
    }
    }
    Method body is copied to the
    call site

    View Slide

  40. Other good-to-know concepts when working with lambdas:

    View Slide

  41. noinline keyword - used to mark lambdas in an inline
    function that we don’t want to inline
    Other good-to-know concepts when working with lambdas:

    View Slide

  42. noinline keyword - used to mark lambdas in an inline
    function that we don’t want to inline
    Other good-to-know concepts when working with lambdas:
    inline fun atLeastAndroidP(action: () -> Unit, noinline fallback: () -> Unit) {
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
    action()
    } else {
    fallback()
    }
    }

    View Slide

  43. noinline keyword - used to mark other lambda params in an
    inline function that we don’t want to inline
    Other good-to-know concepts when working with lambdas:
    inline fun atLeastAndroidP(action: () -> Unit, noinline fallback: () -> Unit) {
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
    action()
    } else {
    fallback()
    }
    }
    the `fallback` lambda is not inlined

    View Slide

  44. non-local returns - typically non-local returns are not
    allowed within lambdas unless the function is inlined.
    Other good-to-know concepts when working with lambdas:

    View Slide

  45. non-local returns - typically non-local returns are not
    allowed within lambdas unless the function is inlined.
    fun useNormalLambda() {
    normalLambda {
    ...
    return@normalLambda // works fine (local return)
    }
    }
    Other good-to-know concepts when working with lambdas:

    View Slide

  46. non-local returns - typically non-local returns are not
    allowed within lambdas unless the function is inlined.
    fun useNormalLambda() {
    normalLambda {
    ...
    return@normalLambda // works fine (local return)
    return // compile error (non-local return)
    }
    }
    Other good-to-know concepts when working with lambdas:

    View Slide

  47. non-local returns - typically non-local returns are not
    allowed within lambdas unless the function is inlined.
    Other good-to-know concepts when working with lambdas:
    fun useNormalLambda() {
    normalLambda {
    ...
    return@normalLambda // works fine (local return)
    return // compile error (non-local return
    }
    }
    fun useInlineLambda() {
    inlineLambda {
    return // works fine. Returns from useInlineLambda
    }
    }

    View Slide

  48. crossinline keyword - used when the lambdas are not used
    directly in the inline function
    Other good-to-know concepts when working with lambdas:

    View Slide

  49. crossinline keyword - used when the lambdas are not used
    directly in the inline function
    Other good-to-know concepts when working with lambdas:
    inline fun View.waitForLayout(crossinline action: () -> Unit) = with(viewTreeObserver) {
    addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
    override fun onGlobalLayout() {
    [email protected](this)
    action()
    }
    })
    }

    View Slide

  50. crossinline keyword - used when the lambdas are not used
    directly in the inline function
    Other good-to-know concepts when working with lambdas:
    inline fun View.waitForLayout(crossinline action: () -> Unit) = with(viewTreeObserver) {
    addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
    override fun onGlobalLayout() {
    [email protected](this)
    action()
    }
    })
    }
    Non-local returns are not allowed
    within the lambda

    View Slide

  51. Standard library functions
    let, run, apply, also

    View Slide

  52. Standard library functions
    Some of these functions are also referred to as Scoping
    functions

    View Slide

  53. let - Standard.kt
    public inline fun T.let(block: (T) -> R): R = block(this)

    View Slide

  54. let - Standard.kt
    public inline fun T.let(block: (T) -> R): R = block(this)
    Lambda function

    View Slide

  55. let - Standard.kt
    public inline fun T.let(block: (T) -> R): R = block(this)
    Receiver

    View Slide

  56. let - Standard.kt
    public inline fun T.let(block: (T) -> R): R = block(this)
    // Sample usage
    client?.email
    ?.let {
    mailer.sendMessage(it, message)
    }

    View Slide

  57. let - Standard.kt
    // Sample usage
    client?.email
    ?.let {
    mailer.sendMessage(it, message)
    }
    public inline fun T.let(block: (T) -> R): R = block(this)

    View Slide

  58. let - Standard.kt
    // Sample usage
    client?.email
    ?.let {
    mailer.sendMessage(it, message)
    }
    // Or
    client?.email
    ?.let { email ->
    mailer.sendMessage(email, message)
    }
    public inline fun T.let(block: (T) -> R): R = block(this)

    View Slide

  59. run - Standard.kt
    public inline fun T.run(block: T.() -> R): R = block()
    Runs the block of code and returns
    the last line in the lambda

    View Slide

  60. run - Standard.kt
    public inline fun T.run(block: T.() -> R): R = block()
    Receiver

    View Slide

  61. run - Standard.kt
    public inline fun T.run(block: T.() -> R): R = block()
    Function literal with
    receiver

    View Slide

  62. run - Standard.kt
    public inline fun T.run(block: T.() -> R): R = block()
    // Sample usage
    fun performTransformation(input: String) : String {...}
    val transformedName = name.run {
    println("Transforming name...")
    val transformedString = performTransformation(this)
    transformedString
    }

    View Slide

  63. run - Standard.kt
    public inline fun T.run(block: T.() -> R): R = block()
    // Sample usage
    fun performTransformation(input: String) : String {...}
    val transformedName = name.run {
    println("Transforming name...")
    val transformedString = performTransformation(this)
    transformedString
    }

    View Slide

  64. run - Standard.kt
    public inline fun T.run(block: T.() -> R): R = block()
    // Sample usage
    fun performTransformation(input: String) : String {...}
    val transformedName = name.run {
    println("Transforming name...")
    val transformedString = performTransformation(this)
    transformedString
    }

    View Slide

  65. run - Standard.kt
    public inline fun T.run(block: T.() -> R): R = block()
    // Sample usage
    fun performTransformation(input: String) : String {...}
    val transformedName = name.run {
    println("Transforming name...")
    val transformedString = performTransformation(this)
    transformedString
    } Receiver is accessed as
    “this”, and exposes the inner
    members of the receiver class

    View Slide

  66. run - Standard.kt
    public inline fun T.run(block: T.() -> R): R = block()
    // Sample usage
    fun performTransformation(input: String) : String {...}
    val transformedName = name.run {
    println("Transforming name...")
    val transformedString = performTransformation(this)
    transformedString
    }
    transformedString is
    returned from the lambda

    View Slide

  67. also - Standard.kt
    inline fun T.also(block: (T) -> Unit): T { block(this); return this }

    View Slide

  68. also - Standard.kt
    inline fun T.also(block: (T) -> Unit): T { block(this); return this }
    // Sample usage
    val dev = Developer().also {
    it.name = "Segun"
    it.lovesCatVideos = true
    it.stack = "Android"
    }

    View Slide

  69. also - Standard.kt
    inline fun T.also(block: (T) -> Unit): T { block(this); return this }
    The receiver is accessed as “it”
    // Sample usage
    val dev = Developer().also {
    it.name = "Segun"
    it.lovesCatVideos = true
    it.stack = "Android"
    }

    View Slide

  70. also - Standard.kt
    inline fun T.also(block: (T) -> Unit): T { block(this); return this }
    // Sample usage
    val dev = Developer().also {
    it.name = "Segun"
    it.lovesCatVideos = true
    it.stack = "Android"
    }
    // OR
    val dev = Developer().also { developer ->
    developer.name = "Segun"
    developer.lovesCatVideos = true
    developer.stack = "Android"
    }

    View Slide

  71. apply - Standard.kt
    inline fun T.apply(block: T.() -> Unit): T { block(); return this }

    View Slide

  72. apply - Standard.kt
    inline fun T.apply(block: T.() -> Unit): T { block(); return this }
    // Sample usage
    val dev = Developer().apply {
    this.name = "Segun"
    this.lovesCatVideos = true
    this.stack = "Android"
    }

    View Slide

  73. apply - Standard.kt
    inline fun T.apply(block: T.() -> Unit): T { block(); return this }
    // Sample usage
    val dev = Developer().apply {
    this.name = "Segun"
    this.lovesCatVideos = true
    this.stack = "Android"
    }
    Exposes the receiver as “this”

    View Slide

  74. Things to consider

    View Slide

  75. Things to consider
    ● Avoid overusing these scoping functions - readability can
    degrade quickly

    View Slide

  76. Things to consider
    ● Avoid overusing these scoping functions - readability can
    degrade quickly
    ● Consider renaming it to something better when possible

    View Slide

  77. Things to consider
    ● Avoid overusing these scoping functions - readability can
    degrade quickly
    ● Consider renaming it to something better when possible
    ● More stdlib functions
    ○ takeIf, takeUnless, with, etc

    View Slide

  78. stdlib functions in action

    View Slide

  79. // Java code
    public void sendMessageToClient(
    @Nullable Client client,
    @Nullable String message,
    @NotNull Mailer mailer
    ) {
    if (client == null || message == null) return;
    PersonalInfo personalInfo = client.getPersonalInfo();
    if (personalInfo == null) return;
    String email = personalInfo.getEmail();
    if (email == null) return;
    mailer.sendMessage(email, message);
    }
    stdlib functions in action

    View Slide

  80. // Java code
    public void sendMessageToClient(
    @Nullable Client client,
    @Nullable String message,
    @NotNull Mailer mailer
    ) {
    if (client == null || message == null) return;
    PersonalInfo personalInfo = client.getPersonalInfo();
    if (personalInfo == null) return;
    String email = personalInfo.getEmail();
    if (email == null) return;
    mailer.sendMessage(email, message);
    }
    // Kotlin equivalent
    fun sendMessageToClient(
    client: Client?,
    message: String?,
    mailer: Mailer
    ) {
    client?.takeIf { message != null }
    ?.personalInfo
    ?.email
    ?.let { email ->
    mailer.sendMessage(email, message!!)
    }
    }
    stdlib functions in action

    View Slide

  81. // Java code
    public void sendMessageToClient(
    @Nullable Client client,
    @Nullable String message,
    @NotNull Mailer mailer
    ) {
    if (client == null || message == null) return;
    PersonalInfo personalInfo = client.getPersonalInfo();
    if (personalInfo == null) return;
    String email = personalInfo.getEmail();
    if (email == null) return;
    mailer.sendMessage(email, message);
    }
    // Kotlin equivalent
    fun sendMessageToClient(
    client: Client?,
    message: String?,
    mailer: Mailer
    ) {
    client?.takeIf { message != null }
    ?.personalInfo
    ?.email
    ?.let { email ->
    mailer.sendMessage(email, message!!)
    }
    }
    stdlib functions in action

    View Slide

  82. // Java code
    public void sendMessageToClient(
    @Nullable Client client,
    @Nullable String message,
    @NotNull Mailer mailer
    ) {
    if (client == null || message == null) return;
    PersonalInfo personalInfo = client.getPersonalInfo();
    if (personalInfo == null) return;
    String email = personalInfo.getEmail();
    if (email == null) return;
    mailer.sendMessage(email, message);
    }
    // Kotlin equivalent
    fun sendMessageToClient(
    client: Client?,
    message: String?,
    mailer: Mailer
    ) {
    client?.takeIf { message != null }
    ?.personalInfo
    ?.email
    ?.let { email ->
    mailer.sendMessage(email, message!!)
    }
    }
    stdlib functions in action

    View Slide

  83. // Java code
    public void sendMessageToClient(
    @Nullable Client client,
    @Nullable String message,
    @NotNull Mailer mailer
    ) {
    if (client == null || message == null) return;
    PersonalInfo personalInfo = client.getPersonalInfo();
    if (personalInfo == null) return;
    String email = personalInfo.getEmail();
    if (email == null) return;
    mailer.sendMessage(email, message);
    }
    // Kotlin equivalent
    fun sendMessageToClient(
    client: Client?,
    message: String?,
    mailer: Mailer
    ) {
    client?.takeIf { message != null }
    ?.personalInfo
    ?.email
    ?.let { email ->
    mailer.sendMessage(email, message!!)
    }
    }
    stdlib functions in action
    Functional, readable, concise

    View Slide

  84. Java & Kotlin Interoperability
    ...a tale of 2 islands

    View Slide

  85. Java & Kotlin Interoperability
    Important because:
    ● Android APIs are written in Java

    View Slide

  86. Java & Kotlin Interoperability
    Important because:
    ● Android APIs are written in Java
    ● Kotlin & Java may be mixed in the same codebase

    View Slide

  87. - nullability
    Java & Kotlin Interoperability

    View Slide

  88. String!
    String?
    What do these mean when you think about nullability &
    types?
    String
    Pop

    View Slide

  89. String!
    String?
    String
    Non-nullable
    type
    Nullable
    type
    Platform type
    Kotlin compiler has
    no idea whether
    it’s nullable or
    not

    View Slide

  90. types are very important when considering
    interoperability between Java & Kotlin

    View Slide

  91. String String? String
    or

    View Slide

  92. // Calling Java from Kotlin
    // Compiles fine. Safe in runtime
    val nameLength = myObject.name.length
    // Java
    @NotNull
    public String getName() {
    return "Segun";
    }
    ...

    View Slide

  93. // Calling Java from Kotlin
    // Compiler error. Need safe access
    val nameLength = myObject.name.length
    // Java
    @Nullable
    public String getName() {
    return null;
    }
    ...

    View Slide

  94. // Calling Java from Kotlin
    // Compiler error. Need safe access
    val nameLength = myObject.name.length
    // Compiles fine. Safe in runtime
    val nameLength = myObject.name?.length
    // Java
    @Nullable
    public String getName() {
    return null;
    }
    ...

    View Slide

  95. // Calling Java from Kotlin
    // Compiles fine. NPE in runtime
    val nameLength = myObject.name.length
    // Java
    public String getName() {
    return null;
    }
    ...

    View Slide

  96. // Java
    public String getName() {
    return null;
    }
    ...
    // Calling Java from Kotlin
    // Compiles fine. NPE in runtime
    val nameLength = myObject.name.length
    // Compiles fine. Safe in runtime
    val nameLength = myObject.name?.length

    View Slide

  97. @Nullable
    String getName() {..}
    Kotlin compiler treats as nullable
    “?”
    @NotNull
    String getName() {..}
    Kotlin compiler treats as
    non-nullable
    String getName() {..} Kotlin compiler has no clue. Treats
    as platform type

    View Slide

  98. - calling Kotlin from Java
    Java & Kotlin Interoperability

    View Slide

  99. Kotlin package-level functions

    View Slide

  100. Kotlin package-level functions
    Functions can be defined in a file directly without being in
    any class or object.
    // InteropDemo.kt
    fun getName() = "Segun"

    View Slide

  101. Kotlin package-level functions
    Functions can be defined in a file directly without being in
    any class or object.
    // InteropDemo.kt
    fun getName() = "Segun"
    // Calling from Java
    InteropDemoKt.getName();

    View Slide

  102. Kotlin package-level functions
    @JvmName annotation helps to tell Kotlin compiler what the
    generated Java class name should be when consumed from Java

    View Slide

  103. Kotlin package-level functions
    // InteropDemo.kt
    @file:JvmName("InteropDemo")
    fun getName() = "Segun"
    @JvmName annotation helps to tell Kotlin compiler what the
    generated Java class name should be when consumed from Java

    View Slide

  104. Kotlin package-level functions
    // InteropDemo.kt
    @file:JvmName("InteropDemo")
    fun getName() = "Segun"
    // Calling from Java
    InteropDemo.getName();
    @JvmName annotation helps to tell Kotlin compiler what the
    generated Java class name should be when consumed from Java

    View Slide

  105. @JvmName
    The annotation can also be applied to functions

    View Slide

  106. @JvmName
    // Kotlin code
    @JvmName("getInteropName")
    fun getName() = "Segun"
    The annotation can also be applied to functions

    View Slide

  107. @JvmName
    // Kotlin code
    @JvmName("getInteropName")
    fun getName() = "Segun"
    // Consumed from Java
    demo.getInteropName();
    The annotation can also be applied to functions

    View Slide

  108. @JvmName
    The annotation can also be applied to functions
    // Kotlin code
    @JvmName("getInteropName")
    fun getName() = "Segun"
    // Consumed from Java
    demo.getInteropName();
    // Consumed from Kotlin
    demo.getName()

    View Slide

  109. @JvmStatic
    Kotlin does not have static methods.
    Instead, it has objects, companion objects

    View Slide

  110. @JvmStatic
    // Kotlin Object
    object StringUtils {
    fun upperCase(text: String) = text.toUpperCase()
    }

    View Slide

  111. @JvmStatic
    // Kotlin Object
    object StringUtils {
    fun upperCase(text: String) = text.toUpperCase()
    }
    // Calling from Java
    String upperCaseName = StringUtils.INSTANCE.upperCase("Segun");

    View Slide

  112. @JvmStatic
    // Kotlin Object
    object StringUtils {
    + @JvmStatic
    fun upperCase(text: String) = text.toUpperCase()
    }
    // Calling from Java
    String upperCaseName = StringUtils.INSTANCE.upperCase("Segun");

    View Slide

  113. @JvmStatic
    // Kotlin Object
    object StringUtils {
    + @JvmStatic
    fun upperCase(text: String) = text.toUpperCase()
    }
    // Calling from Java
    String upperCaseName = StringUtils.INSTANCE.upperCase("Segun");
    String upperCaseName = StringUtils.upperCase("Segun");

    View Slide

  114. @JvmStatic - Static providers in Dagger
    Specially useful when working with Dagger static Providers
    on Android

    View Slide

  115. @Module
    abstract class RepositoryModule {
    @Binds
    abstract fun bindRepo(repository: Repository): IRepository
    companion object {
    // static provider
    @Provides
    fun provideApiService: ApiService = ...
    }
    }
    @JvmStatic - Static providers in Dagger

    View Slide

  116. @Module
    abstract class RepositoryModule {
    @Binds
    abstract fun bindRepo(repository: Repository): IRepository
    companion object {
    // static provider
    @Provides
    fun provideApiService: ApiService = ...
    }
    }
    Won’t compile.
    Dagger error.
    Why?
    @JvmStatic - Static providers in Dagger

    View Slide

  117. @JvmStatic - Static providers in Dagger
    // Generated Java class
    @Module
    public abstract class RepositoryModule {
    ...
    @Binds
    @NotNull
    public abstract IRepository bindRepo(@NotNull Repository var1);
    public static final class Companion {
    @Provides
    @NotNull
    public final ApiService providesApiService() {...}
    }
    }

    View Slide

  118. @JvmStatic - Static providers in Dagger
    // Generated Java class
    @Module
    public abstract class RepositoryModule {
    ...
    @Binds
    @NotNull
    public abstract IRepository bindRepo(@NotNull Repository var1);
    public static final class Companion {
    @Provides
    @NotNull
    public final ApiService providesApiService() {...}
    }
    }
    No @Module

    View Slide

  119. @JvmStatic - Static providers in Dagger
    // Generated Java class
    @Module
    public abstract class RepositoryModule {
    ...
    @Binds
    @NotNull
    public abstract IRepository bindRepo(@NotNull Repository var1);
    public static final class Companion {
    @Provides
    @NotNull
    public final ApiService providesApiService() {...}
    }
    }
    No @Module
    @Provides method is generated as a
    member of the Companion class

    View Slide

  120. @JvmStatic - Static providers in Dagger
    // Generated Java class
    @Module
    public abstract class RepositoryModule {
    ...
    @Binds
    @NotNull
    public abstract IRepository bindRepo(@NotNull Repository var1);
    public static final class Companion {
    @Provides
    @NotNull
    public final ApiService providesApiService() {...}
    }
    }
    No @Module
    @Provides method is generated as a
    member of the Companion class
    Dagger is unable to provide this
    dependency

    View Slide

  121. @JvmStatic - Static providers in Dagger
    @Module
    abstract class RepositoryModule {
    @Binds
    abstract fun bindRepo(repository: Repository): IRepository
    + @Module
    companion object {
    // static provider
    @Provides
    + @JvmStatic
    fun provideApiService: ApiService = ...
    }
    }
    Fixed by adding @Module and @JvmStatic annotations

    View Slide

  122. @JvmStatic - Static providers in Dagger
    Fixed by adding @Module and @JvmStatic annotations
    // Generated Java class with fixes
    @Module
    public abstract class RepositoryModule {
    @Binds
    public abstract IRepository bindRepos(@NotNull Repository var1);
    @Provides
    @JvmStatic
    public static final ApiService providesApiService() {...}
    @Module
    public static final class Companion {
    @Provides
    @JvmStatic
    public final ApiService providesApiService() {...}
    ...
    }
    }
    Kotlin compiler generates a
    static method within the
    RepositoryModule class

    View Slide

  123. @JvmStatic - Static providers in Dagger
    Fixed by adding @Module and @JvmStatic annotations
    // Generated Java class with fixes
    @Module
    public abstract class RepositoryModule {
    @Binds
    public abstract IRepository bindRepos(@NotNull Repository var1);
    @Provides
    @JvmStatic
    public static final ApiService providesApiService() {...}
    @Module
    public static final class Companion {
    @Provides
    @JvmStatic
    public final ApiService providesApiService() {...}
    ...
    }
    }
    Dagger will see this module,
    but generated code will not be
    used.
    RepositoryModule.Companion
    module isn’t registered

    View Slide

  124. @JvmOverloads

    View Slide

  125. @JvmOverloads
    // Kotlin class with default values
    class Point(val x: Int = 0, val y: Int = 0)

    View Slide

  126. @JvmOverloads
    // Kotlin class with default values
    class Point(val x: Int = 0, val y: Int = 0)
    // Calling from Java
    Point p1 = new Point();
    Point p2 = new Point(1, 3);

    View Slide

  127. @JvmOverloads
    // Kotlin class with default values
    class Point(val x: Int = 0, val y: Int = 0)
    // Calling from Java
    Point p1 = new Point();
    Point p2 = new Point(1, 3); Specify none or all params

    View Slide

  128. @JvmOverloads
    // Kotlin class with default values
    class Point @JvmOverloads constructor(val x: Int = 0, val y: Int = 0)

    View Slide

  129. @JvmOverloads
    // Kotlin class with default values
    class Point @JvmOverloads constructor(val x: Int = 0, val y: Int = 0)
    // Generated Java code
    public final class Point {
    // Overloaded constructors
    public Point() {...}
    public Point(int x) {...}
    public Point(int x, int y) {...}
    }

    View Slide

  130. @JvmOverloads
    // Calling from Java
    Point p1 = new Point();
    Point p2 = new Point(1);
    Point p3 = new Point(1, 3);
    // Kotlin class with default values
    class Point @JvmOverloads constructor(val x: Int = 0, val y: Int = 0)
    // Generated Java code
    public final class Point {
    // Overloaded constructors
    public Point() {...}
    public Point(int x) {...}
    public Point(int x, int y) {...}
    }

    View Slide

  131. @JvmOverloads
    Also works on other functions that are not constructors

    View Slide

  132. @Jvm* annotations
    There are more:
    @JvmField
    @JvmSuppressWildcards
    @JvmMultifileClass... etc

    View Slide

  133. Resources
    KotlinConf videos
    #Kotlin on twitter
    Kotlinweekly.net
    kotlinlang.org

    View Slide

  134. Thank you
    segunfamisa
    segunfamisa.com

    View Slide