Kotlin1.4-M1 is released, and finally supports Kotlin interface SAM conversion!

Foreword

An excellent Android development requires a complete  knowledge system . Here, let us grow together as we think.

Finally, the first preview version of Kotlin 1.4 was released. In the new version 1.4-M1, Kotlin added some new features, and at the same time, there are some major improvements. This article will take you to see what features we expect to add and improve in the new version of Kotlin.

1. How to use the new version?

If you use online programming, open https://play.kotlinlang.org/ in your browser, and then you can choose Kotlin version 1.4-M1

If you are using Android Studio or IntelliJ IDE, you can directly upgrade the plug-in to the latest version 1.4-M1, the steps are as follows:

  • 1. 选择Tools -> Kotlin ->Configure Kotlin Plugin Updates.

  • 2. Select from the update list Early Access Preview Xand select the corresponding version

  • 3. Click install to restart, and the configuration is complete.

2. More powerful type recommendation algorithm

In Kotlin1.4, a new and more powerful type recommendation algorithm is used. Perhaps you have already tried this algorithm in Kotlin1.3. In Kotlin1.3, you can achieve it by specifying compiler options. But now it is used by default. For more detailed information about the new algorithm, you can view: https://youtrack.jetbrains.com/issues/KT?q=Tag:%20fixed-in-new-inference%20&_ga=2.58428450.988595807.1586745008-1408654980.1539842787

Only some important improvements are introduced below.

2.1. SAM conversion of Kotlin methods and interfaces

Finally waiting for you, Kotlin1.4 can support Kotlin interface SAM conversion, this is really too important.

What is it SAM转换? There may be some students who do n’t know much about it yet.

SAM conversion, that is, Single Abstract Method Conversions, is the conversion of only a single non-default abstract method interface-for an interface that meets this condition (called SAM Type) , it can be directly expressed in Latda in Kotlin-of course the premise is The type of function represented by Lambda can match the method in the interface.

Before Kotlin 1.4, Kotlin does not support Kotlin's SAM conversion, and can support Java SAM conversion. The official explanation is: ** Kotlin itself already has function types and higher-order functions, no need to go to SAM Transformation. ** This explanation is not for developers, if you have used Java Lambda and Function Interface. When you switch to Kotlin, it will be very dull. It seems that Kotlin realized this, or saw the feedback from the developers, and finally supported it.

What does Kotlin's SAM conversion look like? Look at a comparison together

1.4prior to:

1.4after that:

// 注意需用fun 关键字声明
fun interface Action {
    fun run()
}

fun runAction(a: Action) = a.run()

fun main() {
    // 传递一个对象,OK
    runAction(object : Action{
        override fun run() {
            println("run action")
        }
    })
   // 1.4-M1支持SAM,OK
    runAction {
        println("Hello, Kotlin 1.4!")
    }
}

It can be seen that before 1.4, only one object can be passed, and Kotlin SAM is not supported, and after 1.4, Kotlin SAM can be supported, but there are some changes in usage. Interface needs to be fundeclared with keywords . After the interface is marked with the fun keyword, as long as the interface is used as a parameter, lambda can be passed as a parameter.

2.2. Automatic type inference for more scenes

The new inference algorithm will in many cases 推断类型, in these cases, the old inference needs to explicitly specify their type. For example, in the following example, lambdathe type of the parameter is correctly inferred as String?:

val rulesMap: Map<String, (String?) -> Boolean> = mapOf(
    "weak" to { it != null },
    "medium" to { !it.isNullOrBlank() },
    "strong" to { it != null && "^[a-zA-Z0-9]+$".toRegex().matches(it) }
)

fun main() {
    println(rulesMap.getValue("weak")("abc!"))
    println(rulesMap.getValue("strong")("abc"))
    println(rulesMap.getValue("strong")("abc!"))
}

In version 1.3, the above code IDE是会报错的needs to introduce an explicit lambda parameter or toreplace it with the Pair constructor with explicit generic parameters to make it work. Instead, like this:

//需要显示的lambda 参数
val rulesMap: Map<String, (String?) -> Boolean> = mapOf(
    "weak" to { it -> it != null },
    "medium" to { it -> !it.isNullOrBlank() },
    "strong" to { it ->  it != null && "^[a-zA-Z0-9]+$".toRegex().matches(it) }
)

fun main() {
    println(rulesMap.getValue("weak")("abc!"))
    println(rulesMap.getValue("strong")("abc"))
    println(rulesMap.getValue("strong")("abc!"))
}

The print result is as follows:

true
true
false

Process finished with exit code 0
2.3. Smart type conversion of the last expression in Lambda

In Kotlin 1.3, unless the type is specified, the last expression in the lambda cannot be intelligently cast. Therefore, in the following example, Kotlin 1.3 infers String?the type as the result variable:

val result = run {
    var str = currentValue()
    if (str == null) {
        str = "test"
    }
    str // Kotlin编译器知道str在这里不为null
}
// result的类型在kotlin1.3中推断为String?,在Kotlin1.4中为String

But in Kotlin 1.4, due to the use of a new inference algorithm, lambdathe last internal expression is obtained 智能转换, and this new more precise type is used to infer the resulting lambda type. Therefore, the type of the result variable becomes String. In Kotlin 1.3, it is usually necessary to add explicit coercion ( !!or type such as String 强制转换) to make this situation work, now these coercion is no longer needed.

2.4. Smart conversion of callable type (Callable) reference

Please see the sample code below:

sealed class Animal
class Cat : Animal() {
    fun meow() {
        println("meow")
    }
}

class Dog : Animal() {
    fun woof() {
        println("woof")
    }
}

fun perform(animal: Animal) {
    val kFunction: KFunction<*> = when (animal) {
        is Cat -> animal::meow
        is Dog -> animal::woof
    }
    kFunction.call()
}

fun main() {
    perform(Cat())
}

In kotlin 1.3, you cannot access the members referenced by the smart conversion type, but now it is. In the  Animalvariable intelligently cast to a specific type Catand Dogafter, you can use different members of the references animal :: meowand animal :: woof. After checking the type, you can access the member reference corresponding to the subtype.

2.5. Callable reference optimization

For example, the following example:

fun foo(i: Int = 0): String = "$i!"

fun apply1(func: () -> String): String = func()
fun apply2(func: (Int) -> String): String = func(42)

fun main() {
    println(apply1(::foo))
    println(apply2(::foo))
}

In Kotlin 1.3, the foofunction is interpreted as a function with Int parameters, so it apply1 will report a type error.

image

Now, callable references using functions with default parameter values ​​are optimized, and callable references to foofunctions can be interpreted as 采用一个Int参数OR 不采用任何参数. Therefore, the above type error will not be reported.

2.6. Commission attribute optimization

First look at a piece of code:

fun main() {
    var prop: String? by Delegates.observable(null) { p, old, new ->
        println("$old → $new")
    }
    prop = "abc"
    prop = "xyz"
}

The above code is compiled on Kotlin 1.3, because when analyzing the byfollowing delegate expression, the type of the delegate property will not be considered, so the type error will be reported. But now in kotlin 1.4-M1, the compiler will correctly infer oldand newparameter types String?.

3. Standard library changes

3.1. Abandoned experimental coroutine API

In version 1.3.0, we do not recommend the kotlin.coroutines.experimental API, but recommend it kotlin.coroutines. In 1.4-M1, we will delete from the standard library to kotlin.coroutines.experimentalcomplete deprecation. For those who still use it on the JVM, we provide a compatibility library:  kotlin-coroutines-experimental-compat.jarto replace it. We released it to Bintray along with Kotlin 1.4-M1.

3.2. Delete obsolete modoperators

Another deprecated function is a numeric modoperator, which calculates the remainder after division. In Kotlin 1.1, it was rem()replaced by a function. Now, delete it completely from the standard library.

3.3. Abandoned conversion from floating-point types to Byte and Short

Standard library contains a number of types of floating-point convert integer type of method, such as: toInt()toShort()toByte(). However, due to the narrow value range and small variable size, converting floating-point numbers to Short and Byte may cause unexpected results. To solve this problem, the 1.4-M1, we scrapped Doubleand Floatthe toShort()and toByte()methods. What if you still want to convert floating-point types to Short or Byte? That's also easy to do. For two-step conversion, first convert the floating-point type to Int, and then convert the Int to the target type.

3.4. General launch API

We modified the general reflection API. Now, it contains members available on all three target platforms (JVM, JS, Native), so it is now possible to ensure that the same code works on any of these platforms.

3.5. Proguard configuration for Kotlin reflection

Starting from 1.4-M1, we kotlin-reflect.jarhave embedded Kotlin Reflection Proguard / R8configuration in it. With this change, most Android projects that use R8 or Proguard use kotlin-reflect without any other configuration. You no longer need to copy and paste the Proguard rules of Kotlin reflection. However, please note that you still need to explicitly list all APIs to be considered for reflection.

4. Kotlin/JVM

Starting from version 1.3.70, Kotlin is able to generate type annotations in JVM bytecode (target version 1.8+) so that they are available at runtime. The community has requested this feature for some time because it makes it easier to use some existing Java libraries and provides more extension capabilities for new library authors.

In the following example, String type @Fooannotations can be issued in the bytecode and then used by the library code:

@Target(AnnotationTarget.TYPE)
annotation class Foo

class A {
    fun foo(): @Foo String = "OK"
}

For specific how to use, you can take a look at this blog: https://blog.jetbrains.com/kotlin/2020/03/kotlin-1-3-70-released/#kotlin-jvm

5. Some other changes

In addition to the above changes, there are also some optimizations and improvements for the Kotlin / Js and Kotlin / iOS platforms.

5.1 Kotlin/JS
5.1.1. Gradle DSL changes

In kotlin.jsand multiplatformGradle plug-in, introduced important new settings. Within build.gradle.ktsthe target block of the file, if you want to generate .jsartifacts during the build process , you can configure and use them produceExecutable().

kotlin {
    target {
        useCommonJs()

        produceExecutable()
        
        browser {}
    }
}
  • If you are writing Kotlin / JS library, you can omit the ProduceExecutable()configuration.

  • When using the new IR compiler backend (more details on this, below), omitting this setting means that executable JS files will not be generated (thus, the build process will run faster). A klib file will be generated in the build / libs folder, which can be used from other Kotlin / JS projects or as a dependency in the same project. If you do not specify produceExecutable()it explicitly , this will happen by default.

Using produceExecutable () will generate code that can be executed from the JavaScript ecosystem, whether it has its own entry point or as a JavaScript library, this will generate the actual JavaScript file, which can be run in the node interpreter and can be embedded in the HTML page And executed in the browser, or used as a dependency of the JavaScript project.

5.1.2. New backend

Kotlin 1.4-M1 is the first version that includes a new IR compiler backend for Kotlin / JS targets. This backend is the basis for great improvements and the decisive factor for some changes in the way Kotlin / JS interacts with JavaScript and TypeScript. Several features highlighted below are for the new IR compiler backend. Although it is not enabled by default, we encourage you to try the following in your project.

(1)如何使用新的后端?

gradle.propertiesAdd the following configuration in the configuration file:

kotlin.js.compiler=ir // or both

If you need to generate libraries for the IR compiler backend and the default backend, you can choose to set this flag to both.

For both the function, please see the following chapter introduction.

(2) No binary compatibility

Compared with the original default backend, the main transformation of the new IR compiler backend is that there is no binary compatibility. This lack of compatibility between the two backends of Kotlin / JS means that the new IR compiler backend is used. The created library cannot be used from the default backend and vice versa.

(3) DCE optimization

Compared with the default backend, the new IR compiler backend has been optimized a lot. The generated code works better with the static analyzer. You can even run the generated code from the new IR compiler backend through Google ’s Closure Compiler and use its advanced optimization mode.

(4) Support statement export to JavaScript

Now, declarations marked as public are no longer automatically exported. To make top-level declarations available in JavaScript or TypeScript, use @JsExportannotations.

package blogpost

@JsExport
class KotlinGreeter(private val who: String) {
    fun greet() = "Hello, $who!"
}

@JsExport
fun farewell(who: String) = "Bye, $who!"

fun secretGreeting(who: String) = "Sup, $who!" // only from Kotlin!
(5) Support TypeScript definition

The new compiler supports generating TypeScript definitions from Kotlin code. For configuration produceExecutable()items, and using the top- @JsExportlevel declaration above , a .d.tsfile with TypeScript definitions will be generated . As the above code, the generated file is as follows:

// [...]
namespace blogpost {
    class KotlinGreeter {
        constructor(who: string)
        greet(): string
    }
    function farewell(who: string): string
}
// [...]
6. Some changes in Kotlin / Native
6.1. Objective-C supports generics by default

Early versions of Kotlin provided experimental support for generics in Objective-C interoperability. To generate a frame header with generics from Kotlin code, you must use -Xobjc-genericsoptions. In 1.4-M1, the paradigm is supported by default. But in some cases, this may break existing Objective-C or Swift code that calls the Kotlin framework. If you do n’t want to use the paradigm, add -Xno-objc-genericsoptions

binaries.framework {     freeCompilerArgs += "-Xno-objc-generics"}
6.2. Exception handling changes in Objective-C / Swift interoperation

In 1.4, we slightly changed the way Swift API exception handling is generated from Kotlin. The error handling of Kotlin and Swift is fundamentally different. All Kotlin exceptions are unchecked, while Swift only checks for errors. Therefore, in order to make Swift code aware of exceptions, @ThrowsKotlin functions need to be marked with annotations, which specify a list of potential exception classes.

When compiled to Swift or Objective-C framework, functions that have or are inheriting @Throwsannotations are represented as NSError *processing methods in Objective-C , and as throwsmethods in Swift .

6.3. Performance improvement

We have been working hard to improve the overall performance of Kotlin / Native compilation and execution. In 1.4-M1, we have provided a new object allocator, and in some benchmark tests, it has run twice as fast. Currently, the new dispenser is experimental and not used by default. You can use to -Xallocator = mimallocswitch to this option.

7. Summary

The above are some of the changes of Kotlin1.4-M1, one of the most surprising features is that it finally supports Kotlin interface SAM conversion. You can try some of the other functions. For more detailed information, please go to the official website to find out, and look forward to the release version soon!

Published 488 original articles · praised 85 · 230,000 views +

Guess you like

Origin blog.csdn.net/Coo123_/article/details/105631846