Practical Advanced Kotlin in Practice

Practical Advanced Kotlin in Practice

Note: this is the accompanying slide deck for the workshop repository that you can find at http://bit.ly/advanced-kotlin-workshop

Getting started with Kotlin is as easy as saying “I want lambdas”, sure — but that’s only scratching the surface of the brave new world that adopting Kotlin will open up for Java and Android developers. In this workshop we’ll look at a number of language features and tips to make the most of the Kotlin Programming Language™ in our day-to-day work.We’ll touch on several topics, including property delegates, threading and coroutines, callbacks vs function references, sealed classes, member and extension functions, default parameters, typealiases, data classes and destructuring. For each of them, we’ll go over a series of dos and don’ts and best practices making sure you get the most out of Kotlin.

4580c218737149bf44d012a110612010?s=128

Sebastiano Poggi

August 29, 2019
Tweet

Transcript

  1. 2.

    Topics of the workshop • Properties: lateinit, delegates • Type

    system: type erasure and reified types • Enum vs sealed classes • Functions: top level functions, mixing in some functional code • DSLs • Destructuring and numbered components • Opinions™
  2. 3.

    Properties • val vs var ◦ val is like final

    in Java ◦ Immutable reference ◦ No guarantees on the value immutability ◦ var are mutable variables ◦ Mutable reference and value
  3. 4.

    Properties • What you don’t have a value right away?

    ◦ lateinit var myVar ◦ E.g., Dagger, Context-dependent values, etc ◦ Zero overhead, can change afterwards ◦ Check if initialised with ::myVar.isInitialized ◦ For primitive types use by notNull() instead
  4. 5.

    Properties • What if a value is expensive to produce

    and only used in some cases? ◦ val myVal by lazy { … } • Caches value once calculated, then can’t change anymore • Synchronised (thread-safe) by default ⚠ ◦ Can be made faster with LazyThreadSafetyMode.NONE ◦ Check your threading model!
  5. 6.

    Type system • Inherits JVM limitations ◦ Type erasure loses

    type parameters’ information at runtime • Is stricter in some regards ◦ Has explicit covariance/contravariance keywords: in and out ◦ No need to care much about them... except when you do ◦ Seb’s top tip: try until the compiler is happy
  6. 7.

    Type system • Reified types ◦ Only available for inline

    functions ◦ Allows compiler to resolve type parameters ◦ No need to pass in classes explicitly as arguments ◦ No need to specify type parameters explicitly (in most cases)
  7. 8.

    Enum and sealed classes • Enumerations are enum classes ◦

    They work the same as in Java • Sealed classes ◦ Best of both worlds ◦ Known set of values at compile time ◦ Can be objects or (data) classes or a mix of them • Use sealed classes for “enums with data” ◦ E.g., kotlin.Result
  8. 9.

    Functions • Member functions are declared on a class you

    own • Extension functions can be declared on any class ◦ Even final/closed classes ◦ Statically dispatched ◦ fun MyClass.myFun() → static void myFun(MyClass receiver)
  9. 10.

    Functions • Functions don’t need to be in a class

    ◦ Called top-level functions ◦ Translate to static methods on JVM ◦ In a virtual class called like the package ◦ Functions can take functions as parameters ◦ Higher-order functions
  10. 11.

    DSLs • Create domain-specific languages • Extension functions for scoping

    • Higher-order functions to compose behaviour • E.g.:
 fun user(block: User.() -> Unit): User { … }
 val user = user { name = "Roborbio" }
  11. 12.

    Functional code • Kotlin is OO but has FP facilities

    • Top-level functions • Function types & lambdas • Higher-order functions • Rich collection processing APIs • Sequences • Lazy streams • Can only be “played” once
  12. 13.

    Functional code • Pure functions • Regular functions • No

    “outside” dependencies • No side effects • If you need them, isolate and make them explicit • Great for some use cases • Data processing • Pipelines
  13. 14.

    Collection/sequence APIs • Same idioms as general FP • Mapping,

    filtering, grouping, aggregating, reducing • Collection type-specific APIs • Lists: get by position, sublist, binary search, … • Maps: key/value aware functionality • Sets: intersect, union, subtract
  14. 15.

    Destructuring declarations • Special functions: componentN() • Access constructor properties

    in order • Data classes have them by default • Destructuring declarations • Unpack classes’ components • data class User(val name: String, val surname: String, val email: String)
 val (name, surname) = myUser
  15. 17.

    and now… opinions! In a segment I like to call

    EVERYBODY HAS OPINIONSTM BUT I AM THE SPEAKER SO YOU ALL
 HAVE TO LISTEN TO MY RANTS NOW
  16. 18.

    Everybody has Opinions! • Extension functions • They’re awesome! But…

    • Hard to discover, “break” encapsulation • Avoid abusing them, same as util classes • Prefer smaller scopes • …unless used as entry point to 3rd party API • Consider top-level functions instead • Factor in Java interop
  17. 19.

    Everybody has Opinions! • Delegate properties • They’re cool but

    don’t abuse them • Can obscure meaning and behaviour • Lazy • Don’t make inexpensive stuff lazy • Prefer lateinit for “initialised later” idiom
  18. 20.

    Everybody has Opinions! • DSLs • You don’t always need

    a DSL for that • Avoid being too “smart” and side effects • Operator overloading • Don’t unless it’s extremely obvious what you’re doing • Avoid at all costs if any risk of surprising users • Must be obvious that it’s an overload