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 X
and 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.4
prior to:
1.4
after 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 fun
declared 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, lambda
the 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 to
replace 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, lambda
the 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 Animal
variable intelligently cast to a specific type Cat
and Dog
after, you can use different members of the references animal :: meow
and 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 foo
function is interpreted as a function with Int parameters, so it apply1
will report a type error.
Now, callable references using functions with default parameter values are optimized, and callable references to foo
functions 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 by
following 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 old
and new
parameter 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.experimental
complete deprecation. For those who still use it on the JVM, we provide a compatibility library: kotlin-coroutines-experimental-compat.jar
to replace it. We released it to Bintray along with Kotlin 1.4-M1.
3.2. Delete obsolete mod
operators
Another deprecated function is a numeric mod
operator, 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 Double
and Float
the 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.jar
have embedded Kotlin Reflection Proguard / R8
configuration 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 @Foo
annotations 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.js
and multiplatform
Gradle plug-in, introduced important new settings. Within build.gradle.kts
the target block of the file, if you want to generate .js
artifacts 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.properties
Add 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 @JsExport
annotations.
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- @JsExport
level declaration above , a .d.ts
file 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-generics
options. 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-generics
options
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, @Throws
Kotlin 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 @Throws
annotations are represented as NSError *
processing methods in Objective-C , and as throws
methods 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 = mimalloc
switch 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!