Kotlin 1.3

M1 New Feature

Coroutine

  • buildSequence and buildIterator moved to kotlin.sequences.

Capturing when subject in a variable

  • When のところで変数宣言できるようになった。
fun Request.getBody() =
    when (val response = executeRequest()) {
        is Success -> response.body
        is HttpError -> throw HttpException(response.status)
    }

@JvmStatic and @JvmField can be used in companions of interfaces

  • Interface が JVM Static な フィールド、 メンバ を持てるようになった。 コンパニオンオブジェクトの中で @JvmStatic, @JvmField が使用可能。
    • @JvmStatic を使う場合は、 コンパイラオプション -jvm-target を 1.8 以上にする。
    • @JvmField を使う場合は、 public final val という扱いになる。
interface Service {
    companion object {
        @JvmField
        val ID = 0xF0EF
       
        @JvmStatic
        fun create(): Service = ...
    }
}

Nested declarations in annotation classes

  • annotation class が ネストされたクラス、 インターフェース、 コンパニオンオブジェクトを含むオブジェクトを内包できるようになった。
    • 一つのアノテーションが他のアノテーションをネストできるようになった。
annotation class Outer(val param: Inner) {
    annotation class Inner(val value: String)
}
annotation class Annotation {
    companion object {
        @JvmField
        val timestamp = System.currentTimeMillis()
    }
}
annotation class Annotation {
    companion object {
        @JvmField
        val timestamp = System.currentTimeMillis()
    }
}

Constant of Companion object in Basic type

  • Byte, Short, Int, Long, Char: 定数 SIZE_BITS, SIZE_BYTES
  • Char
    • MIN_VALUE: \u0000
    • MAX_VALUE: \uFFFF

関数型

  • 引数が255個まで使用可能になった。

Extension

  • orEmpty()
    • sequence に追加された
  • isNullOrEmpty
    • colelction, map, array に追加された。

Multiplatform random number generator

  • Example
      val number = Random.nextInt(limit)  // number is in range [0, limit)
    
  • A repeatable random generator initialized with a particular seed can be obtained with the top-level function Random(seed)
      val seriesRng = Random(42)
      val series = List(100) { seriesRng.nextDouble() }
    

SuccessOrFailure

  • SuccessOrFailure
    • inline class The inline class SuccessOrFailure is effectively a discriminated union between successful and failed outcomes of execution of a Kotlin function: Success T | Failure Throwable.

It has been introduced to allow for capturing the result of a function execution, whether successful or not, in order to process it at a later time.

val files = listOf(File("a.txt"), File("b.txt"), File("42.txt"))
val contents: List<SuccessOrFailure<String>> = files.map { runCatching { readFileData(it) } }

println("map successful items:")
val upperCaseContents: List<SuccessOrFailure<String>> = 
    contents.map { r -> r.map { it.toUpperCase() } } 
upperCaseContents.printResults()

println()
println("map successful items catching error from transform operation:")
val intContents: List<SuccessOrFailure<Int>> = 
    contents.map { r -> r.mapCatching { it.toInt() } }
intContents.printResults()

The primary driver for introducing this class is the new Continuation interface, where we want to have a single resumeWith(result: SuccessOrFailure) function instead of two, resume(T) and resumeWithException(Throwable). Note that the Kotlin style guide discourages using SuccessOrFailure type as a return type of Kotlin functions. This is similar to how users are encouraged to have suspend functions return some type instead of plain functins returning a Deferred of that type.

Experimental

M2 Feature

Standard Library

  • UInt
  • UByte
  • UIntArray
    • contentEquals
    • contentToString
    • contentHashCode
    • uintArrayOfZeros
  • UByteArray

Experimental

  • require
    • add smart cast function
    • execute lambda once.
  • Enhanced New Style
    • Example
        fun test(x: List<Int>?) {
            // If the function returns false, the value is definitely not null:
            if (!x.isNullOrEmpty()) {
                println(x.size) // Yay, smart cast to not-null!
            }
        }
              
        fun test(x: Any?) {
            // If the function returns (does not throw), then the argument is true:
            require(x is String) 
            println(x.length) // Smart cast here too!
        }
      
    • affected functions
      • check
      • checkNotNull
      • require
      • requireNotNull
      • kotlin.test.assertTrue
      • kotlin.test.assertFalse
      • kotlin.test.assertNotNull
      • isNullOrEmpty
      • isNullOrBlank
  • Analyzing variable initialization more accurately
    • 変数の初期化状態について、コンパイラがより正確に判定できるようになった。(コンパイラに起因するエラーが出なくなる)
      • 外のスコープの変数をグローバルに更新できるようになるということなので、あまり好ましくはないと思う。
    • affected functions
      • run
      • let
      • with
      • apply
      • also
      • takeIf
      • takeUnless
      • synchronized
    • Example
        val x: Int
        synchronized(lock) {
            x = 42 // The compiler knows that the lambda is invoked exactly once!
        }
        println(x) // Allowed now, the compiler knows that 'x' is definitely assigned.
      
  • Copying elements between two existing arrays
    • array についてのコピーメソッド array.copyInto(targetArray, targetOffset, startIndex, endIndex)
        val sourceArr = arrayOf("k", "o", "t", "l", "i", "n")
        val targetArr = sourceArr.copyInto(arrayOfNulls<String>(6), 3, startIndex = 3, endIndex = 6)
        println(targetArr.contentToString())
             
        sourceArr.copyInto(targetArr, startIndex = 0, endIndex = 3)
        println(targetArr.contentToString())
      

  • associateWith
    • create map with value
      • It was possible to do it before with the associate { it to getValue(it) } function, but now we’re introducing a more efficient and easy to explore alternative: keys.associateWith { getValue(it) }
          val keys = 'a'..'f'
          val map = keys.associateWith { it.toString().repeat(5).capitalize() }
          map.forEach { println(it) }
        
  • ifEmpty, ifBlank functions
    • ifEmpty
      • for Collections, maps, object arrays, char sequences and sequences. now have ifEmpty function, which allows to specify a substitute value that will be used instead of the receiver if it is empty.
        fun printAllUppercase(data: List<String>) {
            val result = data
            .filter { it.all { c -> c.isUpperCase() } }
                  .ifEmpty { listOf("<no uppercase>") }
            result.forEach { println(it) }
        }
          
        printAllUppercase(listOf("foo", "Bar"))
        printAllUppercase(listOf("FOO", "BAR"))
      

      ​ * ifBlank

      • for Char sequences and strings
      val s = "    \n"
      println(s.ifBlank { "<blank>" })
      println(s.ifBlank { null })
    
  • Random API improvements
    • 非負値、非負Byte列を生成できるようになった。
      • nextUInt, nextULong, nextUBytes の例
        val randomUInt = Random.nextUInt(0x8000_0000u..0xFF00_0000u)
        println(randomUInt.toString(16))
        
        val randomUBytes = Random.nextUBytes(4)
        println(randomUBytes.contentToString())
      
    • random: collection, array, range からランダムで要素を取り出すことができる

        val sweets = listOf("nougat", "marshmallow", "oreo", "pie")
        println("I'm going to taste some ${sweets.random()} today!")
       
        val chars = 'a'..'z'
        val randomChars = String(CharArray(10) { chars.random() })
        println(randomChars)
      
  • Sealed classes reflection
    • KClass.sealedSubclasses: Sealed class の 全ての直接のサブタイプ を走査できるクラス。
  • Inline classes In 1.3-M2 we introduce automatic mangling for names of functions that use inline classes in their signatures. We do it to prevent platform signature clashes when there are several overloads which are different only in the inline type, but not in the carrier type:

      // All these types are inline classes mapped to String:
      fun foo(x: UserName) { ... }
      fun foo(x: Login) { ... }
      fun foo(x: UserHash) { ... }
    

    Another reason for mangling is to forbid (accidental) usage from Java, which may be undesired as inline classes are a purely Kotlin concept. Mangled names will have the form of foo-

    • Another two improvements
      • the support for secondary constructors
      • automatically generated toString/equals/hashCode functions

Reference