1. 程式人生 > >Kotlin 1.2 新特性

Kotlin 1.2 新特性

Kotlin JavaScript JVM

點擊關註異步圖書,置頂公眾號

每天與你分享IT好書 技術幹貨 職場知識



技術分享圖片

在Kotlin 1.1中,團隊正式發布了JavaScript目標,允許開發者將Kotlin代碼編譯為JS並在瀏覽器中運行。在Kotlin 1.2中,團隊增加了在JVM和JavaScript之間重用代碼的可能性。現在,使用Kotlin編寫的代碼,可以在所有的應用程序中(包括後端,瀏覽器前端和Android移動應用程序)中重復使用。

技術分享圖片


想要體驗Kotlin1.2新功能的同學,可以下載官方提供的IntelliJ IDEA 2017.3開發工具,或者升級老的IDE,當然也可以通過在線網站來體驗。

跨平臺

跨平臺項目是 Kotlin 1.2 中的一個新的實驗×××,它允許開發者從相同的代碼庫構建應用程序的多個層——後端、前端和Android應用程序,在這個跨平臺方案中,主要包含三個模塊。

  • 通用(common)模塊:包含非特定於任何平臺的代碼,以及不附帶依賴於平臺的 API 實現的聲明。

  • 平臺(platform)模塊:包含用於特定平臺的通用模塊中與平臺相關聲明的實現,以及其他平臺相關代碼。

  • 常規(regular)模塊:針對特定平臺,可以是平臺模塊的某些依賴,也可以是依賴的平臺模塊。


技術分享圖片

要從通用模塊中調用特定於平臺的代碼,可以指定所需的聲明:所有特定於平臺的模塊需要提供實際實現聲明。而在為特定平臺編譯多平臺項目時,會生成通用及特定平臺相關部分的代碼。可以通過 expected 以及 actual 聲明來表達通用代碼對平臺特定部分的依賴關系。expected 聲明指定了一個 API(類、接口、註釋、頂層聲明等)。actual 聲明或是 API 的平臺相關實現,或是在外部庫中 API 現有實現的別名引用。下面是官方提供的相關例子:

通用模塊

// expected platform-specific API:expect fun hello(world: String): Stringfun greet() { // usage of the expected API:
val greeting = hello("multi-platform world")
println(greeting)
}
expect class URL(spec: String) { open fun getHost(): String open fun getPath(): String
}12345678910111213

JVM 平臺代碼

actual fun hello(world: String): String = "Hello, $world, on the JVM platform!"// using existing platform-specific implementation:
actual typealias URL = java.net.URL12345

想要獲取更多跨平臺相關的信息,可以查看官方資料介紹。

請註意,目前跨平臺項目只是一個實驗×××,這意味著該功能已經可以使用,但可能需要在後續版本中更改設計

編譯性能

在1.2的開發過程中,團隊花了很多精力來優化編譯系統,據官方提供的資料顯示,與Kotlin 1.1相比,Kotlin帶來了大約25%的性能提升,並且看到了可以進一步改進的巨大潛力,這些改進將在1.2.x更新中發布。
下圖顯示了使用Kotlin構建兩個大型JetBrains項目的編譯時間差異。

技術分享圖片


語法與庫優化

除了上面介紹的改動之外,Kotlin還在語法層面進行了部分改進,優化的部分有。

通過註解聲明數組變量

自Kotlin1.2開始,系統允許通過註解聲明數組參數,從而取代arrayOf函數的數組聲明方式。例如:

@CacheConfig(cacheNames = ["books", "default"])
public class BookRepositoryImpl {
// ...}1234

可見,新的數組參數聲明語法依賴於註解方式。

關鍵字lateinit

lateinit 和lazy一樣,是 Kotlin中的兩種不同的延遲初始化技術。在Kotlin1.2版本中,使用lateinit修飾符能夠用於全局變量和局部變量了,也就是說,二者都允許延遲初始化。例如,當lambda表達式在構造一個對象時,允許將延遲初始化屬性作為構造參數傳過去。

class Node<T>(val value: T, val next: () -> Node<T>)fun main(args: Array<String>) { // A cycle of three nodes:
lateinit var third: Node<Int>
val second = Node(2, next = { third })
val first = Node(1, next = { second })
third = Node(3, next = { first })
val nodes = generateSequence(first) { it.next() }
println("Values in the cycle: ${nodes.take(7).joinToString { it.value.toString() }}, ...")
}123456789101112131415

運行上面的代碼,輸出結果如下:

Values in the cycle: 1, 2, 3, 1, 2, 3, 1, ...1

延遲初始化屬性檢測

通過訪問屬性的isInitialized字段,現在開發者可以檢查一個延遲初始化屬性是否已經初始化。

class Foo {
lateinit var lateinitVar: String
fun initializationLogic() {
println("isInitialized before assignment: " + this::lateinitVar.isInitialized)
lateinitVar = "value"
println("isInitialized after assignment: " + this::lateinitVar.isInitialized)
}
}
fun main(args: Array<String>) {
Foo().initializationLogic()
}1234567891011121314

運行結果為:

isInitialized before assignment: falseisInitialized after assignment: true12

內聯函數默認參數

自1.2版本開始,Kotlin允許允許給內聯函數的函數參數填寫默認參數了。

inline fun <E> Iterable<E>.strings(transform: (E) -> String = { it.toString() }) =
map { transform(it) }val defaultStrings = listOf(1, 2, 3).strings()val customStrings = listOf(1, 2, 3).strings { "($it)" }
fun main(args: Array<String>) { println("defaultStrings = $defaultStrings")
println("customStrings = $customStrings")123456789

運行結果為:

defaultStrings = [1, 2, 3]customStrings = [(1), (2), (3)]12

變量類型推斷

大家都知道,Kotlin的類型推斷系統是非常強大的,現在Kotlin編譯器也支持通過強制轉換的信息,來推斷出變量類型了。比如說,如果你在調用一個返回“T”的泛型方法時,並將它的返回值“T”轉換為特定類型如“Foo”,編譯器就會推斷出這個方法調用中的“T”其實是“Foo”類型。

這個對安卓開發者而言尤其重要,因為自從API26(Android7.0)開始,findViewById變成了泛型方法,然後編譯器也會正確分析該方法的調用返回值。

val button = findViewById(R.id.button) as Button1

智能轉換

當一個變量為某個安全表達式(如校驗非空)所賦值時,智能轉換也同樣運用於這個安全調用的接收者。

fun countFirst(s: Any): Int { val firstChar = (s as? CharSequence)?.firstOrNull() if (firstChar != null) return s.count { it == firstChar } // 輸入參數s被智能轉換為CharSequence類型
val firstItem = (s as? Iterable<*>)?.firstOrNull() if (firstItem != null) return s.count { it == firstItem } // 輸入參數s被智能轉換為Iterable<*>類型
return -1}fun main(args: Array<String>) { val string = "abacaba"
val countInString = countFirst(string)
println("called on \"$string\": $countInString") val list = listOf(1, 2, 3, 1, 2) val countInList = countFirst(list)
println("called on $list: $countInList")
}12345678910111213141516171819202122

運行結果為:

called on "abacaba": 4called on [1, 2, 3, 1, 2]: 212

另外,Lamba表達式同樣支持對局部變量進行智能轉換,前提是該局部變量只在Lamba表達式之前修改過。

fun main(args: Array<String>) {
val flag = args.size == 0
var x: String? = null
if (flag) x = "Yahoo!"
run { if (x != null) {
println(x.length) // x is smart cast to String
}
}
}12345678910111213

運行結果為:

6

foo的簡寫

為了簡化調用成員的引用,現在可以不用this關鍵字,::foo而不用明確的接收者this::foo。這也使得可調用的引用在你引用外部接收者的成員的lambda中更方便。

棄用

Kotlin1.2版本也棄用了很多不合理的東西。

棄用:枚舉條目中的嵌套類型

在枚舉條目中,inner class由於初始化邏輯中的問題,定義一個非嵌套的類型已經被棄用了。這會在Kotlin 1.2中引起警告,並將在Kotlin 1.3中出錯。

棄用:vararg單個命名參數

為了與註釋中的數組文字保持一致,在命名形式(foo(items = i))中傳遞可變參數的單個項目已被棄用。請使用具有相應數組工廠功能的擴展運算符。

foo(items = *intArrayOf(1))1

在這種情況下,有一種優化可以消除冗余陣列的創建,從而防止性能下降。單參數形式在Kotlin 1.2中產生警告,並將被放在Kotlin 1.3中。

棄用:擴展Throwable的泛型內部類

繼承的泛型類型的內部類Throwable可能會違反類型安全性,因此已被棄用,Kotlin 1.2中有警告,Kotlin 1.3中有錯誤。

棄用:只讀屬性的後臺字段

field = …已經廢棄了在自定義獲取器中分配只讀屬性的後臺字段,Kotlin 1.2中有警告,Kotlin 1.3中有錯誤。

標準庫

Kotlin標準庫與拆分包

Kotlin標準庫現在完全兼容Java 9模塊系統,該系統禁止拆分包(多個jar文件在同一個包中聲明類)。為了支持這一點,新的文物kotlin-stdlib-jdk7 和kotlin-stdlib-jdk8介紹,取代舊的kotlin-stdlib-jre7和kotlin-stdlib-jre8。

為確保與新模塊系統的兼容性,Kotlin做出的另一個更改是將kotlin.reflect從kotlin-reflect庫中移除。如果您正在使用它們,則需要切換到使用kotlin.reflect.full軟件包中的聲明,這是自Kotlin 1.1以來支持的聲明。

窗口,分塊,zipWithNext

為新的擴展Iterable,Sequence以及CharSequence覆蓋這些用例如緩沖或批處理(chunked),滑動窗口和計算滑動平均(windowed),和隨後的項目的處理對(zipWithNext)。

fun main(args: Array<String>) {
val items = (1..9).map { it * it }
val chunkedIntoLists = items.chunked(4)
val points3d = items.chunked(3) { (x, y, z) -> Triple(x, y, z) }
val windowed = items.windowed(4)
val slidingAverage = items.windowed(4) { it.average() }
val pairwiseDifferences = items.zipWithNext { a, b -> b - a }
println("items: $items")
println("chunked into lists: $chunkedIntoLists")
println("3D points: $points3d")
println("windowed by 4: $windowed")
println("sliding average by 4: $slidingAverage")
println("pairwise differences: $pairwiseDifferences")
}123456789101112131415161718

fill, replaceAll, shuffle/shuffled

為了操縱列表,Kotlin加入了一組擴展函數:fill,replaceAll和shuffle對MutableList,shuffled用於只讀List。

fun main(args: Array<String>) {
val items = (1..5).toMutableList() items.shuffle()
println("Shuffled items: $items") items.replaceAll { it * 2 }
println("Items doubled: $items") items.fill(5)
println("Items filled with 5: $items")
}12345678910111213

運行結果為:

Shuffled items: [5, 3, 1, 2, 4]
Items doubled: [10, 6, 2, 4, 8]
Items filled with 5: [5, 5, 5, 5, 5]

數學運算

為了滿足一些特殊的需求,Kotlin 1.2添加了一些常見的數學運算API。

常量:PI和E;

三角函數:cos,sin,tan和它們的反:acos,asin,atan,atan2,

雙曲:cosh,sinh,tanh和它們的反:acosh,asinh,atanh

求冪:pow(擴展函數),sqrt,,hypot ;expexpm1

對數:log,log2,log10,ln,ln1p,

四舍五入: ceil,floor,truncate,round(半連)的功能;
roundToInt,roundToLong(半整數)擴展函數;

符號和絕對值: abs和sign功能; absoluteValue和sign擴展屬性; withSign 擴展功能;max和min兩個價值觀;

二進制表示: ulp 擴展屬性; nextUp,nextDown,nextTowards擴展函數;toBits,toRawBits,Double.fromBits(這些是在kotlin包)。

正則表達式可序列化

現在,Kotlin可以使用Serializable來序列化正則表達式的層次結構。

JVM

構造函數調用規範化

自1.0版以來,Kotlin支持復雜控制流的表達式,例如try-catch表達式和內聯函數調用。但是,如果構造函數調用的參數中存在這樣的表達式時,一些字節碼處理工具不能很好地處理這些代碼。為了緩解這種字節碼處理工具的用戶的這個問題,我們添加了一個命令行選項(-Xnormalize-constructor-calls=MODE),它告訴編譯器為這樣的結構生成更多的類Java字節碼。

其中,這裏的MODE有以下情況:

disable (默認) - 以和Kotlin 1.0和1.1相同的方式生成字節碼;

enable - 為構造函數調用生成類似Java的字節碼。這可以改變類加載和初始化的順序;

preserve-class-initialization -為構造函數調用生成類似Java的字節碼,確保保持類的初始化順序。這可能會影響應用程序的整體性能;只有在多個類之間共享一些復雜的狀態並在類初始化時更新時才使用它。

Java默認方法調用

在Kotlin 1.2之前,接口成員在針對JVM 1.6的情況下重寫Java默認方法會在超級調用上產生一個警告:Super calls to Java default methods are deprecated in JVM target 1.6. Recompile with ‘-jvm-target 1.8’。在Kotlin 1.2中,會出現一個錯誤,因此需要使用JVM target 1.8來編譯這些代碼。

x.equals(null)

調用x.equals(null)上被映射到Java原始(平臺類型Int!,Boolean!,Short!, ,Long!,Float!,Double!)Char!返回不正確true時x為空。從Kotlin 1.2開始,調用x.equals(…)一個平臺類型的null值會拋出一個NPE (但是x == …不會)。

要返回到1.2之前的行為,請將該標誌傳遞-Xno-exception-on-explicit-equals-for-boxed-null給編譯器。

內聯擴展空修復

在以前的版本中,在平臺類型的空值上調用的內聯擴展函數沒有檢查接收器是否為null,並因此允許null轉義到其他代碼中。Kotlin 1.2中強制執行此檢查,如果接收方為空,則拋出異常。

JavaScript

TypedArrays支持

JS類型的數組支持將Kotlin原始數組(例如IntArray,DoubleArray)轉換為JavaScript類型的數組,這以前是可選入功能,默認情況下已啟用。

除此之外,Kotlin的編譯器現在提供一個將所有警告視為錯誤的選項。使用-Werror命令行,或者修改如下配置:

compileKotlin {
kotlinOptions.allWarningsAsErrors = true}123

想要了解更多的官方知識介紹,請查看:Kotlin 1.2帶來了什麽新特性

本文摘自異步社區,作者: xiangzhihong 作品:《Kotlin 1.2 新特性》

技術分享圖片

推薦閱讀

2018年5月新書書單(文末福利)

2018年4月新書書單

異步圖書最全Python書單

一份程序員必備的算法書單

第一本Python神經網絡編程圖書


技術分享圖片

長按二維碼,可以關註我們喲

每天與你分享IT好文。


在“異步圖書”後臺回復“關註”,即可免費獲得2000門在線視頻課程;推薦朋友關註根據提示獲取贈書鏈接,免費得異步e讀版圖書一本。趕緊來參加哦!

點擊閱讀原文,查看更多

閱讀原文


Kotlin 1.2 新特性