前言
Kotlin 是一門開放的語言,不僅僅是源碼的開放,任意使用者都可以直接參與它的建設。大家可以通過 YouTrack 向社群提出自己的 idea 和 issue ,其中一些呼聲高的 issue 會進入 KEEP 交由 Kotlin 團隊管理維護,并有可能被最終實作、出現在未來的某個版本中。
YouTrack Hot Issues
為呼應 “2022技術趨勢” 征文活動(主要為了薅羊毛),本文将透過 YouTrack 中目前最熱門的 issue 大膽預測一下 Kotlin 未來可能引入的新功能:
- 命名空間
- 多接受者擴充函數
- 集合字面值
- 雙類型屬性
- 名字解構
- 異常多重捕獲
- 包級通路
如果你喜歡這些功能,可以去 YouTrack 為它們點贊投票。
命名空間 (Namespace)
youtrack.jetbrains.com/issue/KT-11…
Java 可以通過類名調用 Class 的靜态方法或者靜态變量,這無形中提供了一種 namespace 機制,友善快速索引某個常量或方法。 Kotlin 雖然鼓勵開發者使用頂級方法替代
Util
類,但當這樣的頂級方法太多時,我們也希望有類似的 namespace 機制出現。
目前常見的 workaround 是使用 object 類替代靜态方法,例如
object DisplayUtil{
fun dip2px(context:Context, px:Float) {
...
}
}
Kotlin 标準庫的
Delegates.notNull
、協程庫的
Dispatchers.Default
等都是這樣定義的,這種方式缺點是需要額外建立一個 object 對象,增加記憶體開銷。
也許未來你将看到一種 namespace 關鍵字,像下面這樣使用
namespace DiplayUtil {
fun dip2px(context:Context, px:Float)
}
namespace 可以避免執行個體對象的建立,将在位元組碼階段編譯成 jvm 靜态方法。
多接受者擴充函數 (Multiple receivers)
youtrack.jetbrains.com/issue/KT-10…
有時我們會在 Class 内定義擴充方法
class View {
val Float.dp // Float is an extension receiver for dp property
get() = this * resources.displayMetrics.density
// this refers to Float
// resources, or [email protected], is a property in View
}
這種擴充方法可以在 View 的内部使用,或者在外部借助
with
使用。
with (view) {
42f.dp
}
但實際情況是我們無法為像 View 這樣的三方庫中的類定義擴充方法,是以我們希望有一種能夠定義多 receiver 的擴充方法的機制。
未來有可能會引入
context
關鍵字,通過
context
可以定義兩個甚至多個 receiver
的擴充方法
context(View)
val Float.dp
get() = this * resources.displayMetrics.density
上述代碼等價于
context(View)
fun dp(view: View, float: Float) {
float * view.resources.displayMetrics.density
}
順便提一下,我更喜歡其中另一種建議的文法:
fun (View, Float).dp() = this@Float * [email protected]
雖然由于不符合 Kotlin 的文法習慣,已經被否掉了。。
集合字面值(Collection literals)
youtrack.jetbrains.com/issue/KT-43…
很多現代語言(Python,JS,Go 等等)都支援使用字面值定義集合,例如在 Python 中我們可以這樣定義一個集合:
new_array = ["a", "b", "c"]
字面值的定義方式更加直覺簡潔,這在重資料操作的語言中顯得非常重要。但 Kotlin 目前隻能使用
listOf
,
mapOf
,
setOf
,
intArrayOf
等 builder 方法進行定義,需要開發者記憶多個方法名,而且
varags
也存在一些使用上的隐患。
未來也許我們可以在 Kotlin 引入字面值定義:
val list = [1, 2, 3] // has type of List<Int>
val map = ["one": 1, "two": 2, "three": 3] // has type of Map<String, Int>
以上字面值預設建立 Immutable 的 List 和 Map 類型集合,當然我們也可以在定義的同時明确指定集合類型:
val set: Set<Int> = [1, 2, 3] // creates a Set<Int>
val map: MutableMap<String, Int> = ["one": 1, "two": 2, "three": 3] // creates a MutableMap
數組可以使用下面方式建立,且可以實作類型推到,無需指定泛型
val array = Array [1, 2, 3] // creates Array<Int>, note that "Int" was inferred here
字面值的使用場景不止局限于集合,甚至可以在 data class 等一些“類集合”場景中使用
data class Point(val x: Int, val y: Int)
fun drawLine(from: Point, to: Point)
// Normal Calling
drawLine(Point(1, 2), Point(3, 4))
// Calling with Collection literals
drawLine([1, 2], [3, 4])
雙類型屬性 (public&private property type)
youtrack.jetbrains.com/issue/KT-14…
我們經常有這種需求:一個類的屬性對内使用 Mutable 類型友善使用,而對外隻想暴露 Immutable 類型,避免随意修改,提高安全性。
此時,通常需要我們分别定義兩個成員:
//List
private val _items = mutableListOf<Item>()
val item : List<Item> = _items
//LiveData
private val _list = MutableLiveData<List<News>>()
val list : LiveData<List<News>> get() = _items
由于上述寫法備援,有人為了圖友善可能直接對外暴露 Mutable 類型,容易造成隐患。
未來 Kotlin 或許會引入下面的文法,屬性在定義時對外隻暴露其 Immutable 的抽象類型,保證安全性的同時,減少模闆代碼:
private val items = mutableListOf<Item>()
public get(): List<Item>
名字解構(Name based destructuring)
youtrack.jetbrains.com/issue/KT-19…
我們在通路 data class 或者 Pair、 Triple 等類型時,經常會使用解構文法
data class Address(
val street: String,
val city: String
)
val (myStreet, myCity) = myAddress
目前 Kotlin 使用的基于位置的解構,這存在一定隐患。 比如上述代碼,如果某天我們修改了
Address
,插入了
postalCode
屬性:
data class Address(
val street: String,
val postalCode: String,
val city: String
)
此時,位置解構中的
myCity
将錯誤地擷取
postalCode
的值,編譯器卻無法及時發現問題。此外還有一個突出的問題,當 data class 有更多屬性時,位置結構隻能順序通路,、無法通路某個指定屬性而跳過中間的屬性。
未來 Kotlin 可能會增加基于名字的解構文法:
val (street = myStreet, city = myCity) = myAddress
名字解構中
myCity
中的指派将不會發生上述錯誤,而且當有數量衆多的屬性時也可以更精準地進行指派。
異常多重捕獲 (Multi catch block)
youtrack.jetbrains.com/issue/KT-71…
Java 支援異常多重捕獲,即在一個 catch 塊中捕獲多種異常,而 Kotlin 每個 catch 隻能捕獲一種異常
fun loadData(path: String) {
try {
findDataLoader(path).loadXmlData()
}
catch (e: IOException) {
println("Failed to load configuration from $path: ${e.message}")
}
catch (e: JDOMException) {
println("Failed to load configuration from $path: ${e.message}")
}
}
比如上面例子,當我們加載一個 xml 并使用 jdom 對其進行解析時,我們隻希望捕獲解析相關的錯誤,忽略其它錯誤
fun loadData(path: String) {
try {
findDataLoader(path).loadXmlData()
}
catch (e: IOException) {
println("Failed to load configuration from $path: ${e.message}")
}
catch (e: JDOMException) {
println("Failed to load configuration from $path: ${e.message}")
}
}
上面這樣的寫法顯得比較備援,即便集中到 when 語句處理也會免不了增加一些模闆代碼。
未來 Kotlin 也許會像 Java 那樣支援下面這樣的寫法
fun loadData(path: String) {
try {
findDataLoader(path).loadXmlData()
} catch (e: IOException | JDOMException) {
println("Failed to load configuration from $path: ${e.message}")
}
}
包級通路(Package-private visibility)
youtrack.jetbrains.com/issue/KT-29…
Java 中提供了包級通路控制權限,但是 Kotlin 缺少對應的通路控制符。
internal
會使得整個 Module 都成為可見範圍。 Kotlin 團隊認為以前 Package 是劃分元件的單元,但如今 Module 成為了更常見的元件粒度,通路權限應該圍繞 Module 設計,是以才沒有設計包級通路權限。
雖然這麼說有一定道理,但是很多從 Java 轉到 Kotlin 的開發者仍然習慣于使用 Package 組織工程,包級通路權限将有助于更好地實作代碼隔離,是以不少人仍然希望追加相關能力。
package com.foo
import com.foo.detail.*;
class Public {
private val impl = Detail()
}
// ...
package com.foo.detail
[email protected] class Detail {
// ...
package com.fooimport com.foo.detail.*;class Public { private val impl = Detail()}// ...package [email protected] class Detail {// ...複制代碼