天天看點

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。