天天看点

Kotlin1.4的新特性抢先看

根据KotlinConf的消息,Kotlin1.4会在2020年发布。其中除了很多跨平台能力的补强之外,也有不少语法层面的改进,可能会对广大开发者带来更直接的影响

Kotlin支持SAM转换

 SAM的意思是Singel Abstract Method,我们将只有单一方法的接口/抽象类成为SamType。Kotlin目前(最新1.3.6)仅能支持Java中的SAM转换

//java
public interface OnClickListener {
    void onClick(View v);
}
public class View {
    public void setOnClickListener(@Nullable OnClickListener l) {
        mOnClickListener = l
    }
}

//kt
view.setOnClickListener( object : OnClickListener {
    override fun onClick(v: View) {
        ...
    }
})
// SAM转换 
view.setOnClickListener {
   ...
}
           

Kotlin通过SAM转换可以将Java中对SamType的调用,转换为一个对Lambda的调用,减少大量的模板代码,但是同样SamType如果定义在Kotlin中,是无法转换的

interface Action {
    fun run()
}
fun runAction(a: Action) = a.run()

fun main() {
    runAction {
        // error :Type mismatch.  
        // Required: Action
        // Found:  () → Unit           
       ...
    }
}
           

但是在1.4之后,可以通过为interface添加fun关键字, 声明为SamType,

fun interface Action {
    fun run()
}

fun main() {
    runAction {
        println("Hello, KotlinConf!")
    }
}
           

即使在1.4之前,我们也有一些SAM转换的替代方案,详细可以参考这篇文 https://blog.csdn.net/vitaviva/article/details/104055623

混用命名参数和位置参数

Kotlin进行方法调用时可以通过参数名指定参数,也可以不指定,按照声明的顺序传参

fun f(a: Int, b: Int, c: Int) {}


fun main() {
    f(1, 2, 3) //位置传参:按序传参
    f(a = 1, c = 3, b = 2) //命名传参:按参数名传参
}
           

但是不允许混合两种

fun main() {
    f(1, b = 2, 3) // Mixing named and positioned arguments is not allowed   
}
           

1.4之后允许上面这种传参方式,这样参数过多的调用时可以提高代码可读性。

尾后逗号

当函数的参数比较多时,为了可读性,我们习惯换行书写:

fun f(
    a: Int,
    b: Int,
    c: Int, //error: Expecting an argument
) 

data class Item(
    val a: Int,
    val b: Int,
    val c: Int, //error: Expecting an argument
)
           

此时,如果需要增加参数或者交换位置时,总要额外删除最后一个参数尾部的逗号,1.4之后允许尾后逗号的存在,方便修改。

属性代理优化

我们自定义属性代理时,为了能够在调用getValue/setValue时获取KProperty,Kotlin在编译期会生成一个$$delegatedProperties数组,用来存储所有可能用到KProperty

//定义代理
class Delegate {
    lateinit var real : String
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return real
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("${property.name}: $value")
        real = value
    }


//使用代理
class MyClass {
    var s by Delegat {"hello"}
    s = "hello kotlin"
}
           

反编译java后会发现多了$$delegatedProperties

public final class MyClass {
    static final kotlin.reflect.KProperty[] $$delegatedProperties; //存储KProperty
    ...
    KProperty var2 = $$delegatedProperties[0];
    s.setValue((Object)null, var2, "hello kotlin");
}
           

但很多时候我们不使用KProperty,此时没有必要生成$$delegatedProperties。Kotlin1.4优化了这种case,如果我们在operator前加inline,且使用KProperty的话,就不回再生成$$delegatedProperties。例如 未来1.4中lazy的代理定义如下

//定义 
inline operator fun <T> Lazy<T>.getValue(
        thisRef: Any?, property: KProperty<*>): T = value
           

反编译的Java将不会再生成$$delegatedProperties。