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

Understanding Kotlin Type System

Understanding Kotlin Type System

Kotlin looks magical, but most of it is possible because of it's powerful type system. Understand how it works and how to take advantage of it while coding.

Bruno Aybar

May 15, 2019
Tweet

More Decks by Bruno Aybar

Other Decks in Technology

Transcript

  1. String name = ""; int age = 10; List<String> list

    = ... Class Primitive
 Type Interface
  2. Kotlin build-in types Logical Boolean Integral Byte Char Int Long

    Floating Point Float Double Structured Array String Function types And…? Any Nothing Unit
  3. fun test(param: Any) { } public final static test(Ljava/lang/Object;)V @Lorg/jetbrains/annotations/NotNull;()

    LDC "param" INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull LINENUMBER 3 L1 RETURN L2 LOCALVARIABLE param Ljava/lang/Object; L0 L2 0 bytecode
  4. When we define a class class MyClass We automatically have

    2 types available val a: MyClass val b: MyClass?
  5. When we define a class class MyClass We automatically have

    2 types available val a: MyClass val b: MyClass? … while coding
  6. When we define a class class MyClass After compilation, the

    “non nullable” concept no longer
 exists
  7. fun test(param: Any) { } public final static test(Ljava/lang/Object;)V @Lorg/jetbrains/annotations/NotNull;()

    LDC "param" INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull LINENUMBER 3 L1 RETURN L2 LOCALVARIABLE param Ljava/lang/Object; L0 L2 0 bytecode
  8. fun test(param: Any?) { } public final static test(Ljava/lang/Object;)V @Lorg/jetbrains/annotations/Nullable;()

    // invisible, parameter 0 L0 LINENUMBER 3 L0 RETURN L1 LOCALVARIABLE param Ljava/lang/Object; L0 L1 0 bytecode
  9. data class Data(val name: String?, val lastName: String?) fun hasValidData(data:

    Data?): Boolean { val name = data?.name ?: return false val lastName = data.lastName ?: return false return name.isNotBlank() && lastName.isNotBlank() }
  10. data class Data(val name: String?, val lastName: String?) fun hasValidData(data:

    Data?): Boolean { val name = data?.name ?: return false val lastName = data.lastName ?: return false return name.isNotBlank() && lastName.isNotBlank() } val name = data?.name ?: return false
  11. Why is it possible to assign 
 return 
 to

    a variable? val name: String = data?.name ?: return false
  12. val name = when(number) -> { 1 -> "one" 2

    -> “two" ... } Inferred type?
  13. val message = if(number % 2) "This is an even

    number" else "This is an odd number" Inferred type?
  14. Why is it possible to assign 
 return 
 to

    a variable? val name: String = data?.name ?: return false
  15. val name: String = data?.name ?: return false String Nothing

    Liskov Substitution
 Principle subtype
  16. Liskov Substitution
 Principle if S is a subtype of T,

    
 then objects of type T may be replaced with objects of type S 
 without altering any of the desirable properties of that program
  17. Liskov Substitution
 Principle Fruit Apple Orange val fruit: Fruit =

    Apple() fun peel(fruit: Fruit) { } peel(Fruit())
  18. Liskov Substitution
 Principle Fruit Apple Orange val fruit: Fruit =

    Apple() fun peel(fruit: Fruit) { } peel(Orange())
  19. data class Data(val name: String?, val lastName: String?) fun hasValidData(data:

    Data?): Boolean { val lastName = data.lastName ?: return false return name.isNotBlank() && lastName.isNotBlank() } val name: String = data?.name ?: return false
  20. data class Data(val name: String?, val lastName: String?) fun hasValidData(data:

    Data?): Boolean { val name: String = return false val lastName = data.lastName ?: return false return name.isNotBlank() && lastName.isNotBlank() } Perfectly possible
 (but doesn’t make sense)
  21. Why is it possible to assign 
 return 
 to

    a variable? val name: String = data?.name ?: return false
  22. fun hasValidData(data: Data?): Boolean { val name = data?.name ?:

    throw RuntimeException() val lastName = data.lastName ?: return false return name.isNotBlank() && lastName.isNotBlank() } Throw returns Nothing too
  23. public inline fun TODO(): Nothing = throw NotImplementedError() Kotlin STD

    example fun test(): Boolean { TODO() return true } WARNING: 
 Unreachable code
  24. val name: String = data?.name ?: return false so this

    will actually never be assigned ... because the function ends with the return statement
  25. fun forever(): Nothing { while(true) { } } Or you

    can define your own fun test(): Boolean { forever() return true } WARNING: 
 Unreachable code
  26. fun forever(): Nothing { while(false) { } } What if

    we change the condition? The compiler knows that!
  27. fun forever(): Nothing { while(false) { } } What if

    we change the condition? A ‘return' expression required in a function with a body (‘{…}’)
  28. fun forever(): Nothing { while(false) { } return ????? }

    What if we return something? It is impossible to 
 create an instance of
 Nothing
  29. fun forever(): Nothing? { return null while(true) { } }

    Nothing? ≠ Nothing fun test(): Boolean { forever() return true } NO WARNING: 
 Reachable code
  30. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" else -> "unexpected value: $value" }) fun main() { print(“Message!”) }
  31. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" else -> "unexpected value: $value" }) fun main() { print(“Message!”) } text: Message!
  32. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" else -> "unexpected value: $value" }) fun main() { print(10) }
  33. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" else -> "unexpected value: $value" }) fun main() { print(10) } integer: 10
  34. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" else -> "unexpected value: $value" }) fun main() { print(false) }
  35. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" else -> "unexpected value: $value" }) fun main() { print(false) } Unexpected value: false
  36. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" else -> "unexpected value: $value" }) fun main() { print(null) }
  37. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" else -> "unexpected value: $value" }) fun main() { print(null) } Unexpected value: null
  38. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" is null -> "null value!" else -> "unexpected value: $value" }) fun main() { print(null) }
  39. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" is null -> "null value!" else -> "unexpected value: $value" }) fun main() { print(null) } Type Expected
  40. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" is ???? -> "null value!" else -> "unexpected value: $value" }) fun main() { print(null) }
  41. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value" is Nothing? -> "null value!" else -> "unexpected value: $value" }) fun main() { print(null) }
  42. fun print(value: Any?) = println(when(value) { is String -> "text:

    $value" is Int -> "integer: $value” is Nothing? -> "null value!" else -> "unexpected value: $value" }) fun main() { print(null) } null value!
  43. For every class that supports generics class Response<T> But these

    are val a: Response<Int> val b: Response<Boolean>? ...
  44. For every class that supports generics class Response<T> But these

    are val a: Response<Int> val b: Response<Boolean>? ... … while coding
  45. class Response<T> { fun evaluate() { print("Type: ${T::class.java}") } }

    Cannot use ’T' as a reified parameter.
 Use class instead Type is no 
 longer available!
  46. class Response<T> { fun evaluate() { print("Type: ${T::class.java}") } }

    Cannot use ’T' as a reified parameter.
 Use class instead No work around
  47. fun <T> evaluate(r: Response<T>) { print("Type: ${T::class.java}") } Cannot use

    ’T' as a reified parameter.
 Use class instead
  48. Kotlin build-in types Logical Boolean Integral Byte Char Int Long

    Floating Point Float Double Structured Array String Function types And…? Any Nothing ✔ ✔ Unit
  49. fun test() { } fun test(): Unit { } public

    object Unit { override fun toString() = "kotlin.Unit" }
  50. fun test() { } fun test(): Unit { } These

    are not pure functions,
 they only have side effects
  51. Pure function 1. Given an input, always emits same output

    2. No side effects
 
 3. 100% Unit testable
  52. fun sum(a: Int, b: Int): Int { return a +

    b + random() } Not pure ✘
  53. Not pure ✘ fun sum(a: Int, b: Int): Int {

    print("Side effect!") return a + b }
  54. Not pure ✘ fun sum(a: Int, b: Int): Int {

    updateDatabase(a) return a + b }
  55. Study material - Exploring the Kotlin Type Hierarchy from Top

    to Bottom
 https://www.youtube.com/watch?v=juFkdMv4B9s 
 - Programmer dictionary: Class vs Type vs Object
 https://blog.kotlin-academy.com/programmer-dictionary-class-vs-type-vs- object-e6d1f74d1e2e - An Illustrated Guide to Covariance and Contravariance
 in Kotlin https://typealias.com/guides/illustrated-guide-covariance- contravariance/
 - Getting Real with Kotlin's Reified Type Parameters
 https://typealias.com/guides/getting-real-with-reified-type-parameters/