$30 off During Our Annual Pro Sale. View Details »

What's New in Kotlin 1.3? - Chicago Kotlin User Group

What's New in Kotlin 1.3? - Chicago Kotlin User Group

Kotlin 1.3 is finally here and all of the new features aren't going to explain themselves. I gave this presentation to the Chicago Kotlin User Group on 2018-11-20.

Todd Ginsberg

November 20, 2018
Tweet

More Decks by Todd Ginsberg

Other Decks in Technology

Transcript

  1. What’s New In
    Kotlin 1.3?
    Chicago Kotlin User Group
    2018-11-20
    Todd Ginsberg
    @ToddGinsberg
    Principal Software Developer

    View Slide

  2. @ToddGinsberg
    Who Is This Person?
    Todd Ginsberg
    Principal Software Developer @Netspend
    (a payments company in Austin, TX)
    Chicago Java User Group Community Director and CFO

    View Slide

  3. @ToddGinsberg
    What’s New?

    View Slide

  4. @ToddGinsberg
    Start at the Beginning…
    fun main(args: Array) {
    }

    View Slide

  5. @ToddGinsberg
    Start at the Beginning…
    fun main() {
    }

    View Slide

  6. @ToddGinsberg
    Capture the Subject of When
    val answer = theAnswer()
    return when(answer) {
    42 -> "You got the right answer!"
    else -> "Sorry, $answer is not correct"
    }

    View Slide

  7. @ToddGinsberg
    Capture the Subject of When
    when(val answer = theAnswer()) {
    42 -> "You got the right answer!"
    else -> "Sorry, $answer is not correct"
    }

    View Slide

  8. @ToddGinsberg
    Function: associateWith()
    val people = listOf("Alice", "Bob", "Charlie")
    val nameAndLength: Map =
    people.associate { it to it.length }

    View Slide

  9. @ToddGinsberg
    Function: associateWith()
    val people = listOf("Alice", "Bob", "Charlie")
    val nameAndLength: Map =
    people.associateWith { it.length }

    View Slide

  10. @ToddGinsberg
    isNullOrEmpy() on Collections
    maybeEmptyArray()?.isNullOrEmpty()
    maybeEmptyMap()?.isNullOrEmpty()
    maybeEmptyCollection()?.isNullOrEmpty()

    View Slide

  11. @ToddGinsberg
    Empty Collections and Maps
    arrayOf().ifEmpty { arrayOf(1) }
    listOf().ifEmpty { listOf(1) }
    setOf().ifEmpty { setOf(1) }
    mapOf().ifEmpty { mapOf("A" to 1) }

    View Slide

  12. @ToddGinsberg
    Empty Sequences
    val s : Sequence? =
    maybeEmptySequence()

    View Slide

  13. @ToddGinsberg
    Empty Sequences
    val s : Sequence =
    maybeEmptySequence()
    .orEmpty()

    View Slide

  14. @ToddGinsberg
    Empty Sequences
    val s : Sequence =
    maybeEmptySequence()
    .orEmpty()
    .ifEmpty { someDefaultSequence() }

    View Slide

  15. @ToddGinsberg
    Empty Strings
    val s: String =
    calculateAnswer()
    .ifEmpty { "Default" }

    View Slide

  16. @ToddGinsberg
    Blank Strings
    val s: String =
    calculateAnswer()
    .ifEmpty { "Default" }
    .ifBlank { "Default" }

    View Slide

  17. @ToddGinsberg
    Nullable Any HashCode
    val myHashCode: Int = myObject?.hashCode() ?: 0

    View Slide

  18. @ToddGinsberg
    Nullable Any HashCode
    public inline fun Any?.hashCode(): Int =
    this?.hashCode() ?: 0
    val myHashCode: Int = myObject.hashCode()

    View Slide

  19. @ToddGinsberg
    Boolean Companion
    fun Boolean.Companion.`¯\_(ツ)_/¯`(): Boolean =
    Random.nextBoolean()
    val majorLifeDecision = Boolean.`¯\_(ツ)_/¯`()

    View Slide

  20. @ToddGinsberg
    Boolean Companion
    @SinceKotlin("1.3")
    companion object {}

    View Slide

  21. @ToddGinsberg
    Basic Type Constants
    SIZE_BYTES and SIZE_BITS available on
    • Char
    • Byte
    • Short
    • Int
    • Long
    Char: 16 bits, 2 bytes
    Byte: 8 bits, 1 byte
    Short: 16 bits, 2 bytes
    Int: 32 bits, 4 bytes
    Long: 64 bits, 8 bytes

    View Slide

  22. @ToddGinsberg
    Multiplatform random()
    import kotlin.random.*
    val random = Random.nextInt(10)

    View Slide

  23. @ToddGinsberg
    Multiplatform random()
    public abstract class Random {
    public open fun nextInt(): Int = nextBits(32)
    companion object Default : Random() {
    // ...
    }
    }

    View Slide

  24. @ToddGinsberg
    Multiplatform random()
    companion object Default : Random() {
    private val defaultRandom: Random =
    defaultPlatformRandom()
    override fun nextInt(): Int =
    defaultRandom.nextInt()
    }
    internal expect fun defaultPlatformRandom(): Random

    View Slide

  25. @ToddGinsberg
    Multiplatform random()
    val winner = arrayOf(”A", ”B", ”C").random()
    val winner = listOf(”A", ”B", ”C").random()
    val letters = 'A' .. 'Z'
    val randomWord = (0..10)
    .map { letters.random() }
    .joinToString(separator = "")

    View Slide

  26. @ToddGinsberg
    Aritiy > 22
    GOOD NEWS! We are no longer limited to 22 function arguments!
    val crazy = {
    p1: Int, p2: Int, p3: Int, p4: Int, p5: Int,
    p6: Int, p7: Int, p8: Int, p9: Int, p10: Int,
    p11: Int, p12: Int, p13: Int, p14: Int, p15: Int,
    p16: Int, p17: Int, p18: Int, p19: Int, p20: Int,
    p21: Int, p22: Int -> true
    }

    View Slide

  27. @ToddGinsberg
    Aritiy > 22
    GOOD NEWS! We are no longer limited to 22 function arguments!
    crazy(
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
    12, 13, 14, 15, 16, 17, 18, 19, 20,
    21, 22
    )

    View Slide

  28. @ToddGinsberg
    Aritiy > 22
    BAD NEWS – We are now limited to 254 function arguments
    val totallyReasonableThingToDo =
    { p1: Int, p2: Int, p3: Int, p4: Int, p5: Int, p6: Int, p7: Int, p8: Int, p9: Int, p10: Int,
    p11: Int, p12: Int, p13: Int, p14: Int, p15: Int, p16: Int, p17: Int, p18: Int, p19: Int, p20: Int,
    p21: Int, p22: Int, p23: Int, p24: Int, p25: Int, p26: Int, p27: Int, p28: Int, p29: Int, p30: Int,
    p31: Int, p32: Int, p33: Int, p34: Int, p35: Int, p36: Int, p37: Int, p38: Int, p39: Int, p40: Int,
    p41: Int, p42: Int, p43: Int, p44: Int, p45: Int, p46: Int, p47: Int, p48: Int, p49: Int, p50: Int,
    p51: Int, p52: Int, p53: Int, p54: Int, p55: Int, p56: Int, p57: Int, p58: Int, p59: Int, p60: Int,
    p61: Int, p62: Int, p63: Int, p64: Int, p65: Int, p66: Int, p67: Int, p68: Int, p69: Int, p70: Int,
    p71: Int, p72: Int, p73: Int, p74: Int, p75: Int, p76: Int, p77: Int, p78: Int, p79: Int, p80: Int,
    p81: Int, p82: Int, p83: Int, p84: Int, p85: Int, p86: Int, p87: Int, p88: Int, p89: Int, p90: Int,
    p91: Int, p92: Int, p93: Int, p94: Int, p95: Int, p96: Int, p97: Int, p98: Int, p99: Int, p100: Int,
    p101: Int, p102: Int, p103: Int, p104: Int, p105: Int, p106: Int, p107: Int, p108: Int, p109: Int, p110: Int,
    p111: Int, p112: Int, p113: Int, p114: Int, p115: Int, p116: Int, p117: Int, p118: Int, p119: Int, p120: Int,
    p121: Int, p122: Int, p123: Int, p124: Int, p125: Int, p126: Int, p127: Int, p128: Int, p129: Int, p130: Int,
    p131: Int, p132: Int, p133: Int, p134: Int, p135: Int, p136: Int, p137: Int, p138: Int, p139: Int, p140: Int,
    p141: Int, p142: Int, p143: Int, p144: Int, p145: Int, p146: Int, p147: Int, p148: Int, p149: Int, p150: Int,
    p151: Int, p152: Int, p153: Int, p154: Int, p155: Int, p156: Int, p157: Int, p158: Int, p159: Int, p160: Int,
    p161: Int, p162: Int, p163: Int, p164: Int, p165: Int, p166: Int, p167: Int, p168: Int, p169: Int, p170: Int,
    p171: Int, p172: Int, p173: Int, p174: Int, p175: Int, p176: Int, p177: Int, p178: Int, p179: Int, p180: Int,
    p181: Int, p182: Int, p183: Int, p184: Int, p185: Int, p186: Int, p187: Int, p188: Int, p189: Int, p190: Int,
    p191: Int, p192: Int, p193: Int, p194: Int, p195: Int, p196: Int, p197: Int, p198: Int, p199: Int, p200: Int,
    p201: Int, p202: Int, p203: Int, p204: Int, p205: Int, p206: Int, p207: Int, p208: Int, p209: Int, p210: Int,
    p211: Int, p212: Int, p213: Int, p214: Int, p215: Int, p216: Int, p217: Int, p218: Int, p219: Int, p220: Int,
    p221: Int, p222: Int, p223: Int, p224: Int, p225: Int, p226: Int, p227: Int, p228: Int, p229: Int, p230: Int,
    p231: Int, p232: Int, p233: Int, p234: Int, p235: Int, p236: Int, p237: Int, p238: Int, p239: Int, p240: Int,
    p241: Int, p242: Int, p243: Int, p244: Int, p245: Int, p246: Int, p247: Int, p248: Int, p249: Int, p250: Int,
    p251: Int, p252: Int, p253: Int, p254: Int ->
    true
    }

    View Slide

  29. @ToddGinsberg
    Aritiy > 22
    BAD NEWS – We are now limited to 254 function arguments
    totallyReasonableThingToDo(
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
    61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
    81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
    101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
    121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
    141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
    161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
    201, 202, 203, 204, 205, 206, 207, 208, 209, 200, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
    221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
    241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254
    )

    View Slide

  30. @ToddGinsberg
    Aritiy > 22
    /** A function that takes 0 arguments. */
    public interface Function0 : Function
    {
    public operator fun invoke(): R
    }

    View Slide

  31. @ToddGinsberg
    Aritiy > 22
    /** A function that takes 1 argument. */
    public interface Function1 : Function
    {
    public operator fun invoke(p1: P1): R
    }

    View Slide

  32. @ToddGinsberg
    Aritiy > 22
    interface FunctionN : Function {
    operator fun invoke(vararg args: Any?): R
    override val arity: Int
    }

    View Slide

  33. @ToddGinsberg
    Sealed Class Reflection
    sealed class Expr
    data class Const(val number: Double) : Expr()
    data class Sum(val e1: Expr, val e2: Expr) : Expr()
    object NotANumber : Expr()
    println(Expr::class
    .sealedSubclasses
    .map { it.simpleName }
    )
    // [Const, Sum, NotANumber]

    View Slide

  34. @ToddGinsberg
    Nested Declarations in Annotations
    annotation class AsyncWork(
    val threads: Int = maxThreads
    ) {
    companion object {
    const val maxThreads: Int = 8
    }
    }

    View Slide

  35. @ToddGinsberg
    Nested Declarations in Annotations
    annotation class Transaction(
    val isolation: Isolation
    ) {
    enum class Isolation {
    CREATE_NEW,
    REUSE_EXISTING, NONE
    }
    }
    @Transaction(Transaction.Isolation.CREATE_NEW)
    class Example

    View Slide

  36. @ToddGinsberg
    Nested Declarations in Annotations
    annotation class Settings {
    annotation class Name(val name: String)
    annotation class PasswordStrategy(
    val minChars: Int,
    val minComplexity: Double = 1.0
    )
    }
    @Settings.Name("Some kind of provider")
    @Settings.PasswordStrategy(minChars = 10)
    class Provider

    View Slide

  37. @ToddGinsberg
    Progressive Mode
    • Compiler Arguments:
    • -progressive
    • -Xprogressive
    • Trade-offs:
    • Bug fixes now
    • Possibly broken code

    View Slide

  38. @ToddGinsberg
    Compiler Arguments
    • Compiler Arguments in a File
    • Existing: -Xargfile=filename.txt
    • New: @filename.txt

    View Slide

  39. @ToddGinsberg
    Graduated

    View Slide

  40. @ToddGinsberg
    Start at the Beginning…
    suspend fun main(args: Array) {
    }

    View Slide

  41. @ToddGinsberg
    Start at the Beginning…
    suspend fun main() {
    }

    View Slide

  42. @ToddGinsberg
    Coroutines!
    suspend fun doSomeWork(): Int {
    val a1 = async { doSomeLongRunningWork(42) }
    val a2 = async { doSomeLongRunningWork(806) }
    return a1.await() + a2.await()
    }

    View Slide

  43. @ToddGinsberg
    Coroutines!
    suspend fun doSomeWork(): Int {
    val a1 = async { doSomeLongRunningWork(42) }
    val a2 = async { doSomeLongRunningWork(806) }
    return a1.await() + a2.await()
    }

    View Slide

  44. @ToddGinsberg
    Structured Concurrency
    suspend fun doSomeWork(): Int =
    coroutineScope {
    val a1 = async { doSomeLongRunningWork(42) }
    val a2 = async { doSomeLongRunningWork(806) }
    a1.await() + a2.await()
    }

    View Slide

  45. @ToddGinsberg
    Structured Concurrency
    GlobalScope.launch {
    doSomeWork()
    }

    View Slide

  46. @ToddGinsberg
    Changes to Lazy Sequences
    fun lazyFib(): Sequence = buildSequence {
    var state = Pair(0, 1)
    while(true) {
    yield(state.second)
    state = Pair(
    state.second, state.first + state.second
    )
    }
    }

    View Slide

  47. @ToddGinsberg
    Changes to Lazy Sequences
    fun lazyFib(): Sequence = sequence {
    var state = Pair(0, 1)
    while(true) {
    yield(state.second)
    state = Pair(
    state.second, state.first + state.second
    )
    }
    }

    View Slide

  48. @ToddGinsberg
    Experimental

    View Slide

  49. @ToddGinsberg
    Inline Classes
    fun canVote(years: Int): Boolean =
    years >= 18
    inline class Age(val years: Int)
    fun canVote(age: Age): Boolean =
    age.years >= 18

    View Slide

  50. @ToddGinsberg
    Inline Classes
    inline class Age(val years: Int) {
    fun minor(): Boolean = years < 18
    }
    fun canVote(age: Age): Boolean =
    !age.minor()

    View Slide

  51. @ToddGinsberg
    Inline Classes
    • Must have a public constructor with a single val
    • No init block
    • Must be final
    • Cannot extend anything
    • Cannot have any additional properties
    • Must be a top level class
    • Cannot have inner classes
    • Cannot be defined with recursively defined generics
    Limitations!

    View Slide

  52. @ToddGinsberg
    Unsigned Types
    val aUInt: UInt = 42u
    val aULong: ULong = 42uL

    View Slide

  53. @ToddGinsberg
    Unsigned Types
    val myUByte: UByte = someByte.toUByte()
    val myUShort: UShort = someShort.toUShort()
    val myUInt: UInt = someInt.toUInt()
    val myULong: ULong = someLong.toULong()

    View Slide

  54. @ToddGinsberg
    Unsigned Types
    val uByte: UByte = "255".toUByte()
    val uShort: UShort = "65535".toUShort()
    val uInt: UInt = "4294967295".toUInt()
    val uLong: ULong = "18446744073709551615".toULong()

    View Slide

  55. @ToddGinsberg
    Unsigned Types
    As well as…
    • Hex Literals = 0xFFFF_FFFFu
    • Unsigned Arrays = uintArrayOf(1u, 2u, 3u)
    • Unsigned Ranges = (0u .. UInt.MAX_VALUE)
    • Remove warnings with @ExperimentalUnsignedTypes

    View Slide

  56. @ToddGinsberg
    Contracts
    fun CharSequence?.isNullOrBlank() : Boolean =
    this == null || this.isBlank()
    val s: String? = calculateString()
    if(!s.isNullOrBlank()) {
    s?.reversed()
    }

    View Slide

  57. @ToddGinsberg
    Contracts
    @ExperimentalContracts
    fun CharSequence?.isNullOrBlank() : Boolean {
    contract {
    returns (false) implies
    (this@isNullOrBlank != null)
    }
    return this == null || this.isBlank()
    }

    View Slide

  58. @ToddGinsberg
    Contracts
    @ExperimentalContracts
    fun main(args: Array) {
    val s: String? = calculateString()
    if(!s.isNullOrBlank()) {
    s.reversed()
    }
    }

    View Slide

  59. @ToddGinsberg
    Contracts
    fun main(args: Array) {
    val myValue: String
    run {
    myValue = "Hello, World!”
    }
    }

    View Slide

  60. @ToddGinsberg
    Contracts
    @ExperimentalContracts
    inline fun run(block: () -> R): R {
    contract {
    callsInPlace(
    block,
    InvocationKind.EXACTLY_ONCE
    )
    }
    return block()
    }

    View Slide

  61. @ToddGinsberg
    Experimental Annotations
    @Experimental(level = Experimental.Level.WARNING)
    annotation class MyExperimentalApi
    @MyExperimentalApi
    class SomeExperimentalThing {
    // ...
    }

    View Slide

  62. @ToddGinsberg
    Experimental Annotations
    @MyExperimentalApi
    fun doSomething(n: SomeExperimentalThing) {
    // ...
    }
    @UseExperimental(MyExperimentalApi::class)
    fun doSomethingElse() {
    SomeExperimentalThing().doSomething()
    }

    View Slide

  63. @ToddGinsberg
    More Details
    https://todd.ginsberg.com/post/kotlin-1.3-features/

    View Slide

  64. Than You!
    @ToddGinsberg
    https://todd.ginsberg.com
    [email protected]

    View Slide