# Kotlin 1.1 的新特性
## 目錄
[TOC]
## JavaScrip
從 Kotlin 1.1 開始,JavaScript 目標平臺不再當是實驗性的。所有語言功能都支持,并且有許多新的工具用于與前端開發環境集成。更詳細改動列表,請參見下文。
## 協程(實驗性的)
Kotlin 1.1 的關鍵新特性是*協程*,它帶來了 `future`/`await`、 `yield` 以及類似的編程模式的支持。Kotlin 的設計中的關鍵特性是協程執行的實現是語言庫的一部分,而不是語言的一部分,所以你不必綁定任何特定的編程范式或并發庫。
協程實際上是一個輕量級的線程,可以掛起并稍后恢復。協程通過[*掛起函數*](http://www.hmoore.net/alex_wsc/android_kotlin/1318237)支持:對這樣的函數的調用可能會掛起協程,并啟動一個新的協程,我們通常使用匿名掛起函數(即掛起 lambda 表達式)。
我們來看看在外部庫 [kotlinx.coroutines](https://github.com/kotlin/kotlinx.coroutines) 中實現的 `async`/`await`:
```kotlin
// 在后臺線程池中運行該代碼
fun asyncOverlay() = async(CommonPool) {
// 啟動兩個異步操作
val original = asyncLoadImage("original")
val overlay = asyncLoadImage("overlay")
// 然后應用疊加到兩個結果
applyOverlay(original.await(), overlay.await())
}
// 在 UI 上下文中啟動新的協程
launch(UI) {
// 等待異步疊加完成
val image = asyncOverlay().await()
// 然后在 UI 中顯示
showImage(image)
}
```
這里,`async { …… }` 啟動一個協程,當我們使用 `await()` 時,掛起協程的執行,而執行正在等待的操作,并且在等待的操作完成時恢復(可能在不同的線程上) 。
標準庫通過 `yield` 和 `yieldAll` 函數使用協程來支持*惰性生成序列*。在這樣的序列中,在取回每個元素之后掛起返回序列元素的代碼塊,并在請求下一個元素時恢復。這里有一個例子:
```kotlin
import kotlin.coroutines.experimental.*
fun main(args: Array<String>) {
//sampleStart
val seq = buildSequence {
for (i in 1..5) {
// 產生一個 i 的平方
yield(i * i)
}
// 產生一個區間
yieldAll(26..28)
}
// 輸出該序列
println(seq.toList())
//sampleEnd
}
```
運行上面的代碼以查看結果。隨意編輯它并再次運行!
更多信息請參見[協程文檔](http://www.hmoore.net/alex_wsc/android_kotlin/1318237)及[教程](https://www.kotlincn.net/docs/tutorials/coroutines/coroutines-basic-jvm.html)。
請注意,協程目前還是一個**實驗性的特性**,這意味著 Kotlin 團隊不承諾在最終的 1.1 版本時保持該功能的向后兼容性。
## 其他語言功能
### 類型別名
類型別名允許你為現有類型定義備用名稱。這對于泛型類型(如集合)以及函數類型最有用。
這里有幾個例子
```kotlin
//sampleStart
typealias OscarWinners = Map<String, String>
fun countLaLaLand(oscarWinners: OscarWinners) =
oscarWinners.count { it.value.contains("La La Land") }
// 請注意,類型名稱(初始名和類型別名)是可互換的:
fun checkLaLaLandIsTheBestMovie(oscarWinners: Map<String, String>) =
oscarWinners["Best picture"] == "La La Land"
//sampleEnd
fun oscarWinners(): OscarWinners {
return mapOf(
"Best song" to "City of Stars (La La Land)",
"Best actress" to "Emma Stone (La La Land)",
"Best picture" to "Moonlight" /* …… */)
}
fun main(args: Array<String>) {
val oscarWinners = oscarWinners()
val laLaLandAwards = countLaLaLand(oscarWinners)
println("LaLaLandAwards = $laLaLandAwards (in our small example), but actually it's 6.")
val laLaLandIsTheBestMovie = checkLaLaLandIsTheBestMovie(oscarWinners)
println("LaLaLandIsTheBestMovie = $laLaLandIsTheBestMovie")
}
```
更詳細信息請參閱其 [KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/type-aliases.md)。
### 已綁定的可調用引用
現在可以使用 `::` 操作符來獲取指向特定對象實例的方法或屬性的[成員引用](http://www.hmoore.net/alex_wsc/android_kotlin/1318347)。以前這只能用 lambda 表達式表示。
這里有一個例子:
```kotlin
//sampleStart
val numberRegex = "\\d+".toRegex()
val numbers = listOf("abc", "123", "456").filter(numberRegex::matches)
//sampleEnd
fun main(args: Array<String>) {
println("Result is $numbers")
}
```
更詳細信息請參閱其 [KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/bound-callable-references.md)。
### 密封類和數據類
Kotlin 1.1 刪除了一些對 Kotlin 1.0 中已存在的密封類和數據類的限制。現在你可以在同一個文件中的任何地方定義一個密封類的子類,而不只是以作為密封類嵌套類的方式。數據類現在可以擴展其他類。這可以用來友好且清晰地定義一個表達式類的層次結構:
```kotlin
//sampleStart
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
val e = eval(Sum(Const(1.0), Const(2.0)))
//sampleEnd
fun main(args: Array<String>) {
println("e is $e") // 3.0
}
```
更詳細信息請參閱其[文檔](http://www.hmoore.net/alex_wsc/android_kotlin/1318307)或者[密封類](https://github.com/Kotlin/KEEP/blob/master/proposals/sealed-class-inheritance.md)及[數據類](https://github.com/Kotlin/KEEP/blob/master/proposals/data-class-inheritance.md)的 KEEP。
### lambda 表達式中的解構
現在可以使用[解構聲明](http://www.hmoore.net/alex_wsc/android_kotlin/1318339)語法來解開傳遞給 lambda 表達式的參數。
這里有一個例子:
```kotlin
fun main(args: Array<String>) {
//sampleStart
val map = mapOf(1 to "one", 2 to "two")
// 之前
println(map.mapValues { entry ->
val (key, value) = entry
"$key -> $value!"
})
// 現在
println(map.mapValues { (key, value) -> "$key -> $value!" })
//sampleEnd
}
```
更詳細信息請參閱其[文檔](http://www.hmoore.net/alex_wsc/android_kotlin/1318339)及其 [KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/destructuring-in-parameters.md)。
### 下劃線用于未使用的參數
對于具有多個參數的 lambda 表達式,可以使用 `_` 字符替換不使用的參數的名稱:
```kotlin
fun main(args: Array<String>) {
val map = mapOf(1 to "one", 2 to "two")
//sampleStart
map.forEach { _, value -> println("$value!") }
//sampleEnd
}
```
這也適用于[解構聲明](http://www.hmoore.net/alex_wsc/android_kotlin/1318339):
```kotlin
data class Result(val value: Any, val status: String)
fun getResult() = Result(42, "ok").also { println("getResult() returns $it") }
fun main(args: Array<String>) {
//sampleStart
val (_, status) = getResult()
//sampleEnd
println("status is '$status'")
}
```
更詳細信息請參閱其 [KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/underscore-for-unused-parameters.md)。
### 數字字面值中的下劃線
正如在 Java 8 中一樣,Kotlin 現在允許在數字字面值中使用下劃線來分隔數字分組:
```kotlin
//sampleStart
val oneMillion = 1_000_000
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
//sampleEnd
fun main(args: Array<String>) {
println(oneMillion)
println(hexBytes.toString(16))
println(bytes.toString(2))
}
```
更詳細信息請參閱其 [KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/underscores-in-numeric-literals.md)。
### 對于屬性的更短語法
對于沒有自定義訪問器、或者將 getter 定義為表達式主體的屬性,現在可以省略屬性的類型:
```kotlin
//sampleStart
data class Person(val name: String, val age: Int) {
val isAdult get() = age >= 20 // 屬性類型推斷為 “Boolean”
}
//sampleEnd
fun main(args: Array<String>) {
val akari = Person("Akari", 26)
println("$akari.isAdult = ${akari.isAdult}")
}
```
### 內聯屬性訪問器
如果屬性沒有幕后字段,現在可以使用 `inline` 修飾符來標記該屬性訪問器。這些訪問器的編譯方式與[內聯函數](http://www.hmoore.net/alex_wsc/android_kotlin/1318318)相同。
```kotlin
//sampleStart
public val <T> List<T>.lastIndex: Int
inline get() = this.size - 1
//sampleEnd
fun main(args: Array<String>) {
val list = listOf('a', 'b')
// 其 getter 會內聯
println("Last index of $list is ${list.lastIndex}")
}
```
你也可以將整個屬性標記為 `inline`——這樣修飾符應用于兩個訪問器。更詳細信息請參閱其[文檔](http://www.hmoore.net/alex_wsc/android_kotlin/1318318)及其 [KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/inline-properties.md)。
### 局部委托屬性
現在可以對局部變量使用[委托屬性](http://www.hmoore.net/alex_wsc/android_kotlin/1318315)語法。一個可能的用途是定義一個延遲求值的局部變量:
```kotlin
import java.util.Random
fun needAnswer() = Random().nextBoolean()
fun main(args: Array<String>) {
//sampleStart
val answer by lazy {
println("Calculating the answer...")
42
}
if (needAnswer()) { // 返回隨機值
println("The answer is $answer.") // 此時計算出答案
}
else {
println("Sometimes no answer is the answer...")
}
//sampleEnd
}
```
更詳細信息請參閱其 [KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/local-delegated-properties.md)。
### 委托屬性綁定的攔截
對于[委托屬性](http://www.hmoore.net/alex_wsc/android_kotlin/1318315),現在可以使用 `provideDelegate` 操作符攔截委托到屬性之間的綁定。
例如,如果我們想要在綁定之前檢測屬性名稱,我們可以這樣寫:
```kotlin
class ResourceLoader<T>(id: ResourceID<T>) {
operator fun provideDelegate(thisRef: MyUI, property: KProperty<*>): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, property.name)
…… // 屬性創建
}
private fun checkProperty(thisRef: MyUI, name: String) { …… }
}
fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { …… }
class MyUI {
val image by bindResource(ResourceID.image_id)
val text by bindResource(ResourceID.text_id)
}
```
`provideDelegate` 方法在創建 `MyUI` 實例期間將會為每個屬性調用,并且可以立即執行必要的驗證。
更詳細信息請參閱其[文檔](http://www.hmoore.net/alex_wsc/android_kotlin/1318315)。
### 泛型枚舉值訪問
現在可以用泛型的方式來對枚舉類的值進行枚舉:
```kotlin
//sampleStart
enum class RGB { RED, GREEN, BLUE }
inline fun <reified T : Enum<T>> printAllValues() {
print(enumValues<T>().joinToString { it.name })
}
//sampleEnd
fun main(args: Array<String>) {
printAllValues<RGB>() // 輸出 RED, GREEN, BLUE
}
```
### 對于 DSL 中隱式接收者的作用域控制
`@DslMarker` 注解允許限制來自 DSL 上下文中的外部作用域的接收者的使用。考慮那個典型的 [HTML 構建器示例](type-safe-builders.html):
```kotlin
table {
tr {
td { + "Text" }
}
}
```
在 Kotlin 1.0 中,傳遞給 `td` 的 lambda 表達式中的代碼可以訪問三個隱式接收者:傳遞給 `table`、`tr`和 `td` 的。 這允許你調用在上下文中沒有意義的方法——例如在 `td` 里面調用 `tr`,從而在 `<td>` 中放置一個 `<tr>` 標簽。
在 Kotlin 1.1 中,你可以限制這種情況,以使只有在 `td` 的隱式接收者上定義的方法會在傳給 `td` 的 lambda 表達式中可用。你可以通過定義標記有 `@DslMarker` 元注解的注解并將其應用于標記類的基類。
更詳細信息請參閱其[文檔](http://www.hmoore.net/alex_wsc/android_kotlin/1318349)及其 [KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/scope-control-for-implicit-receivers.md)。
### `rem` 操作符
`mod` 操作符現已棄用,而使用 `rem` 取代。動機參見[這個問題](https://youtrack.jetbrains.com/issue/KT-14650)。
## 標準庫
### 字符串到數字的轉換
在 String 類中有一些新的擴展,用來將它轉換為數字,而不會在無效數字上拋出異常:
`String.toIntOrNull(): Int?`、 `String.toDoubleOrNull(): Double?` 等。
```kotlin
val port = System.getenv("PORT")?.toIntOrNull() ?: 80
```
還有整數轉換函數,如 `Int.toString()`、 `String.toInt()`、 `String.toIntOrNull()`,每個都有一個帶有 `radix` 參數的重載,它允許指定轉換的基數(2 到 36)。
### onEach()
`onEach` 是一個小、但對于集合和序列很有用的擴展函數,它允許對操作鏈中的集合/序列的每個元素執行一些操作,可能帶有副作用。
對于迭代其行為像 `forEach` 但是也進一步返回可迭代實例。 對于序列它返回一個包裝序列,它在元素迭代時延遲應用給定的動作。
```kotlin
inputDir.walk()
.filter { it.isFile && it.name.endsWith(".txt") }
.onEach { println("Moving $it to $outputDir") }
.forEach { moveFile(it, File(outputDir, it.toRelativeString(inputDir))) }
```
### also()、takeIf() 和 takeUnless()
這些是適用于任何接收者的三個通用擴展函數。
`also` 就像 `apply`:它接受接收者、做一些動作、并返回該接收者。二者區別是在 `apply` 內部的代碼塊中接收者是 `this`,而在 `also` 內部的代碼塊中是 `it`(并且如果你想的話,你可以給它另一個名字)。當你不想掩蓋來自外部作用域的 `this` 時這很方便:
```kotlin
class Block {
lateinit var content: String
}
//sampleStart
fun Block.copy() = Block().also {
it.content = this.content
}
//sampleEnd
// 使用“apply”代替
fun Block.copy1() = Block().apply {
this.content = this@copy1.content
}
fun main(args: Array<String>) {
val block = Block().apply { content = "content" }
val copy = block.copy()
println("Testing the content was copied:")
println(block.content == copy.content)
}
```
`takeIf` 就像單個值的 `filter`。它檢測接收者是否滿足該謂詞,并在滿足時返回該接收者否則不滿足時返回 `null`。
結合 elvis-操作符和及早返回,它允許編寫如下結構:
```kotlin
val outDirFile = File(outputDir.path).takeIf { it.exists() } ?: return false
// 對現有的 outDirFile 做些事情
```
```kotlin
fun main(args: Array<String>) {
val input = "Kotlin"
val keyword = "in"
//sampleStart
val index = input.indexOf(keyword).takeIf { it >= 0 } ?: error("keyword not found")
// 對輸入字符串中的關鍵字索引做些事情,鑒于它已找到
//sampleEnd
println("'$keyword' was found in '$input'")
println(input)
println(" ".repeat(index) + "^")
}
```
`takeUnless` 與 `takeIf` 相同,只是它采用了反向謂詞。當它 _不_ 滿足謂詞時返回接收者,否則返回 `null`。因此,上面的示例之一可以用 `takeUnless` 重寫如下:
```kotlin
val index = input.indexOf(keyword).takeUnless { it < 0 } ?: error("keyword not found")
```
當你有一個可調用的引用而不是 lambda 時,使用也很方便:
```kotlin
private fun testTakeUnless(string: String) {
//sampleStart
val result = string.takeUnless(String::isEmpty)
//sampleEnd
println("string = \"$string\"; result = \"$result\"")
}
fun main(args: Array<String>) {
testTakeUnless("")
testTakeUnless("abc")
}
```
### groupingBy()
此 API 可以用于按照鍵對集合進行分組,并同時折疊每個組。 例如,它可以用于計算文本中字符的頻率:
```kotlin
fun main(args: Array<String>) {
val words = "one two three four five six seven eight nine ten".split(' ')
//sampleStart
val frequencies = words.groupingBy { it.first() }.eachCount()
//sampleEnd
println("Counting first letters: $frequencies.")
// 另一種方式是使用“groupBy”和“mapValues”創建一個中間的映射,
// 而“groupingBy”的方式會即時計數。
val groupBy = words.groupBy { it.first() }.mapValues { (_, list) -> list.size }
println("Comparing the result with using 'groupBy': ${groupBy == frequencies}.")
}
```
### Map.toMap() 和 Map.toMutableMap()
這倆函數可以用來簡易復制映射:
```kotlin
class ImmutablePropertyBag(map: Map<String, Any>) {
private val mapCopy = map.toMap()
}
```
### Map.minus(key)
運算符 `plus` 提供了一種將鍵值對添加到只讀映射中以生成新映射的方法,但是沒有一種簡單的方法來做相反的操作:從映射中刪除一個鍵采用不那么直接的方式如 `Map.filter()` 或 `Map.filterKeys()`。
現在運算符 `minus` 填補了這個空白。有 4 個可用的重載:用于刪除單個鍵、鍵的集合、鍵的序列和鍵的數組。
```kotlin
fun main(args: Array<String>) {
//sampleStart
val map = mapOf("key" to 42)
val emptyMap = map - "key"
//sampleEnd
println("map: $map")
println("emptyMap: $emptyMap")
}
```
### minOf() 和 maxOf()
這些函數可用于查找兩個或三個給定值中的最小和最大值,其中值是原生數字或 `Comparable` 對象。每個函數還有一個重載,它接受一個額外的 `Comparator` 實例,如果你想比較自身不可比的對象的話。
```kotlin
fun main(args: Array<String>) {
//sampleStart
val list1 = listOf("a", "b")
val list2 = listOf("x", "y", "z")
val minSize = minOf(list1.size, list2.size)
val longestList = maxOf(list1, list2, compareBy { it.size })
//sampleEnd
println("minSize = $minSize")
println("longestList = $longestList")
}
```
### 類似數組的列表實例化函數
類似于 `Array` 構造函數,現在有創建 `List` 和 `MutableList` 實例的函數,并通過調用 lambda 表達式來初始化每個元素:
```kotlin
fun main(args: Array<String>) {
//sampleStart
val squares = List(10) { index -> index * index }
val mutable = MutableList(10) { 0 }
//sampleEnd
println("squares: $squares")
println("mutable: $mutable")
}
```
### Map.getValue()
`Map` 上的這個擴展函數返回一個與給定鍵相對應的現有值,或者拋出一個異常,提示找不到該鍵。如果該映射是用 `withDefault` 生成的,這個函數將返回默認值,而不是拋異常。
```kotlin
fun main(args: Array<String>) {
//sampleStart
val map = mapOf("key" to 42)
// 返回不可空 Int 值 42
val value: Int = map.getValue("key")
val mapWithDefault = map.withDefault { k -> k.length }
// 返回 4
val value2 = mapWithDefault.getValue("key2")
// map.getValue("anotherKey") // <- 這將拋出 NoSuchElementException
//sampleEnd
println("value is $value")
println("value2 is $value2")
}
```
### 抽象集合
這些抽象類可以在實現 Kotlin 集合類時用作基類。對于實現只讀集合,有 `AbstractCollection`、 `AbstractList`、 `AbstractSet` 和 `AbstractMap`,而對于可變集合,有 `AbstractMutableCollection`、 `AbstractMutableList`、 `AbstractMutableSet` 和 `AbstractMutableMap`。在 JVM 上,這些抽象可變集合從 JDK 的抽象集合繼承了大部分的功能。
### 數組處理函數
標準庫現在提供了一組用于逐個元素操作數組的函數:比較(`contentEquals` 和 `contentDeepEquals`),哈希碼計算(`contentHashCode` 和 `contentDeepHashCode`),以及轉換成一個字符串(`contentToString` 和`contentDeepToString`)。它們都支持 JVM(它們作為 `java.util.Arrays` 中的相應函數的別名)和 JS(在Kotlin 標準庫中提供實現)。
```kotlin
fun main(args: Array<String>) {
//sampleStart
val array = arrayOf("a", "b", "c")
println(array.toString()) // JVM 實現:類型及哈希亂碼
println(array.contentToString()) // 良好格式化為列表
//sampleEnd
}
```
## JVM 后端
### Java 8 字節碼支持
Kotlin 現在可以選擇生成 Java 8 字節碼(命令行選項 `-jvm-target 1.8` 或者 Ant/Maven/Gradle 中的相應選項)。目前這并不改變字節碼的語義(特別是,接口和 lambda 表達式中的默認方法的生成與 Kotlin 1.0 中完全一樣),但我們計劃在以后進一步使用它。
### Java 8 標準庫支持
現在有支持在 Java 7 和 8 中新添加的 JDK API 的標準庫的獨立版本。如果你需要訪問新的 API,請使用 `kotlin-stdlib-jre7` 和 `kotlin-stdlib-jre8` maven 構件,而不是標準的 `kotlin-stdlib`。這些構件是在 `kotlin-stdlib` 之上的微小擴展,它們將它作為傳遞依賴項帶到項目中。
### 字節碼中的參數名
Kotlin 現在支持在字節碼中存儲參數名。這可以使用命令行選項 `-java-parameters` 啟用。
### 常量內聯
編譯器現在將 `const val` 屬性的值內聯到使用它們的位置。
### 可變閉包變量
用于在 lambda 表達式中捕獲可變閉包變量的裝箱類不再具有 volatile 字段。此更改提高了性能,但在一些罕見的使用情況下可能導致新的競爭條件。如果受此影響,你需要提供自己的同步機制來訪問變量。
### javax.scripting 支持
Kotlin 現在與[javax.script API](https://docs.oracle.com/javase/8/docs/api/javax/script/package-summary.html)(JSR-223)集成。
其 API 允許在運行時求值代碼段:
```kotlin
val engine = ScriptEngineManager().getEngineByExtension("kts")!!
engine.eval("val x = 3")
println(engine.eval("x + 2")) // 輸出 5
```
關于使用 API 的示例項目參見[這里](https://github.com/JetBrains/kotlin/tree/master/libraries/examples/kotlin-jsr223-local-example)。
### kotlin.reflect.full
[為 Java 9 支持準備](https://blog.jetbrains.com/kotlin/2017/01/kotlin-1-1-whats-coming-in-the-standard-library/),在 `kotlin-reflect.jar` 庫中的擴展函數和屬性已移動到 `kotlin.reflect.full` 包中。舊包(`kotlin.reflect`)中的名稱已棄用,將在Kotlin 1.2 中刪除。請注意,核心反射接口(如 `KClass`)是 Kotlin 標準庫(而不是 `kotlin-reflect`)的一部分,不受移動影響。
## JavaScript 后端
### 統一的標準庫
Kotlin 標準庫的大部分目前可以從代碼編譯成 JavaScript 來使用。
特別是,關鍵類如集合(`ArrayList`、 `HashMap` 等)、異常(`IllegalArgumentException` 等)以及其他幾個關鍵類(`StringBuilder`、 `Comparator`)現在都定義在 `kotlin` 包下。在 JVM 平臺上,一些名稱是相應 JDK 類的類型別名,而在 JS 平臺上,這些類在 Kotlin 標準庫中實現。
### 更好的代碼生成
JavaScript 后端現在生成更加可靜態檢測的代碼,這對 JS 代碼處理工具(如minifiers、 optimisers、 linters 等)更加友好。
### `external` 修飾符
如果你需要以類型安全的方式在 Kotlin 中訪問 JavaScript 實現的類,你可以使用 `external` 修飾符寫一個 Kotlin 聲明。(在 Kotlin 1.0 中,使用了 `@native` 注解。)與 JVM 目標平臺不同,JS 平臺允許對類和屬性使用 external 修飾符。例如,可以按以下方式聲明 DOM `Node` 類:
```kotlin
external class Node {
val firstChild: Node
fun appendChild(child: Node): Node
fun removeChild(child: Node): Node
// 等等
}
```
### 改進的導入處理
現在可以更精確地描述應該從 JavaScript 模塊導入的聲明。
如果在外部聲明上添加 `@JsModule("<模塊名>")` 注解,它會在編譯期間正確導入到模塊系統(CommonJS或AMD)。例如,使用 CommonJS,該聲明會通過 `require(……)` 函數導入。
此外,如果要將聲明作為模塊或全局 JavaScript 對象導入,可以使用 `@JsNonModule` 注解。
例如,以下是將 JQuery 導入 Kotlin 模塊的方法:
```kotlin
external interface JQuery {
fun toggle(duration: Int = definedExternally): JQuery
fun click(handler: (Event) -> Unit): JQuery
}
@JsModule("jquery")
@JsNonModule
@JsName("$")
external fun jquery(selector: String): JQuery
```
在這種情況下,JQuery 將作為名為 `jquery` 的模塊導入。或者,它可以用作 $-對象,這取決于Kotlin編譯器配置使用哪個模塊系統。
你可以在應用程序中使用如下所示的這些聲明:
```kotlin
fun main(args: Array<String>) {
jquery(".toggle-button").click {
jquery(".toggle-panel").toggle(300)
}
}
```
- 前言
- Kotlin簡介
- IntelliJ IDEA技巧總結
- idea設置類注釋和方法注釋模板
- 像Android Studion一樣創建工程
- Gradle
- Gradle入門
- Gradle進階
- 使用Gradle創建一個Kotlin工程
- 環境搭建
- Androidstudio平臺搭建
- Eclipse的Kotlin環境配置
- 使用IntelliJ IDEA
- Kotlin學習路線
- Kotlin官方中文版文檔教程
- 概述
- kotlin用于服務器端開發
- kotlin用于Android開發
- kotlin用于JavaScript開發
- kotlin用于原生開發
- Kotlin 用于數據科學
- 協程
- 多平臺
- 新特性
- 1.1的新特性
- 1.2的新特性
- 1.3的新特性
- 開始
- 基本語法
- 習慣用法
- 編碼規范
- 基礎
- 基本類型
- 包與導入
- 控制流
- 返回與跳轉
- 類與對象
- 類與繼承
- 屬性與字段
- 接口
- 可見性修飾符
- 擴展
- 數據類
- 密封類
- 泛型
- 嵌套類
- 枚舉類
- 對象
- 類型別名
- 內嵌類
- 委托
- 委托屬性
- 函數與Lambda表達式
- 函數
- Lambda表達式
- 內聯函數
- 集合
- 集合概述
- 構造集合
- 迭代器
- 區間與數列
- 序列
- 操作概述
- 轉換
- 過濾
- 加減操作符
- 分組
- 取集合的一部分
- 取單個元素
- 排序
- 聚合操作
- 集合寫操作
- List相關操作
- Set相關操作
- Map相關操作
- 多平臺程序設計
- 平臺相關聲明
- 以Gradle創建
- 更多語言結構
- 解構聲明
- 類型檢測與轉換
- This表達式
- 相等性
- 操作符重載
- 空安全
- 異常
- 注解
- 反射
- 作用域函數
- 類型安全的構造器
- Opt-in Requirements
- 核心庫
- 標準庫
- kotlin.test
- 參考
- 關鍵字與操作符
- 語法
- 編碼風格約定
- Java互操作
- Kotlin中調用Java
- Java中調用Kotlin
- JavaScript
- 動態類型
- kotlin中調用JavaScript
- JavaScript中調用kotlin
- JavaScript模塊
- JavaScript反射
- JavaScript DCE
- 原生
- 并發
- 不可變性
- kotlin庫
- 平臺庫
- 與C語言互操作
- 與Object-C及Swift互操作
- CocoaPods集成
- Gradle插件
- 調試
- FAQ
- 協程
- 協程指南
- 基礎
- 取消與超時
- 組合掛起函數
- 協程上下文與調度器
- 異步流
- 通道
- 異常處理與監督
- 共享的可變狀態與并發
- Select表達式(實驗性)
- 工具
- 編寫kotlin代碼文檔
- 使用Kapt
- 使用Gradle
- 使用Maven
- 使用Ant
- Kotlin與OSGI
- 編譯器插件
- 編碼規范
- 演進
- kotlin語言演進
- 不同組件的穩定性
- kotlin1.3的兼容性指南
- 常見問題
- FAQ
- 與Java比較
- 與Scala比較(官方已刪除)
- Google開發者官網簡介
- Kotlin and Android
- Get Started with Kotlin on Android
- Kotlin on Android FAQ
- Android KTX
- Resources to Learn Kotlin
- Kotlin樣品
- Kotlin零基礎到進階
- 第一階段興趣入門
- kotlin簡介和學習方法
- 數據類型和類型系統
- 入門
- 分類
- val和var
- 二進制基礎
- 基礎
- 基本語法
- 包
- 示例
- 編碼規范
- 代碼注釋
- 異常
- 根類型“Any”
- Any? 可空類型
- 可空性的實現原理
- kotlin.Unit類型
- kotlin.Nothing類型
- 基本數據類型
- 數值類型
- 布爾類型
- 字符型
- 位運算符
- 變量和常量
- 語法和運算符
- 關鍵字
- 硬關鍵字
- 軟關鍵字
- 修飾符關鍵字
- 特殊標識符
- 操作符和特殊符號
- 算術運算符
- 賦值運算符
- 比較運算符
- 邏輯運算符
- this關鍵字
- super關鍵字
- 操作符重載
- 一元操作符
- 二元操作符
- 字符串
- 字符串介紹和屬性
- 字符串常見方法操作
- 字符串模板
- 數組
- 數組介紹創建及遍歷
- 數組常見方法和屬性
- 數組變化以及下標越界問題
- 原生數組類型
- 區間
- 正向區間
- 逆向區間
- 步長
- 類型檢測與類型轉換
- is、!is、as、as-運算符
- 空安全
- 可空類型變量
- 安全調用符
- 非空斷言
- Elvis操作符
- 可空性深入
- 可空性和Java
- 函數
- 函數式編程概述
- OOP和FOP
- 函數式編程基本特性
- 組合與范疇
- 在Kotlin中使用函數式編程
- 函數入門
- 函數作用域
- 函數加強
- 命名參數
- 默認參數
- 可變參數
- 表達式函數體
- 頂層、嵌套、中綴函數
- 尾遞歸函數優化
- 函數重載
- 控制流
- if表達式
- when表達式
- for循環
- while循環
- 循環中的 Break 與 continue
- return返回
- 標簽處返回
- 集合
- list集合
- list集合介紹和操作
- list常見方法和屬性
- list集合變化和下標越界
- set集合
- set集合介紹和常見操作
- set集合常見方法和屬性
- set集合變換和下標越界
- map集合
- map集合介紹和常見操作
- map集合常見方法和屬性
- map集合變換
- 集合的函數式API
- map函數
- filter函數
- “ all ”“ any ”“ count ”和“ find ”:對集合應用判斷式
- 別樣的求和方式:sumBy、sum、fold、reduce
- 根據人的性別進行分組:groupBy
- 扁平化——處理嵌套集合:flatMap、flatten
- 惰性集合操作:序列
- 區間、數組、集合之間轉換
- 面向對象
- 面向對象-封裝
- 類的創建及屬性方法訪問
- 類屬性和字段
- 構造器
- 嵌套類(內部類)
- 枚舉類
- 枚舉類遍歷&枚舉常量常用屬性
- 數據類
- 密封類
- 印章類(密封類)
- 面向對象-繼承
- 類的繼承
- 面向對象-多態
- 抽象類
- 接口
- 接口和抽象類的區別
- 面向對象-深入
- 擴展
- 擴展:為別的類添加方法、屬性
- Android中的擴展應用
- 優化Snackbar
- 用擴展函數封裝Utils
- 解決煩人的findViewById
- 擴展不是萬能的
- 調度方式對擴展函數的影響
- 被濫用的擴展函數
- 委托
- 委托類
- 委托屬性
- Kotlin5大內置委托
- Kotlin-Object關鍵字
- 單例模式
- 匿名類對象
- 伴生對象
- 作用域函數
- let函數
- run函數
- with函數
- apply函數
- also函數
- 標準庫函數
- takeIf 與 takeUnless
- 第二階段重點深入
- Lambda編程
- Lambda成員引用高階函數
- 高階函數
- 內聯函數
- 泛型
- 泛型的分類
- 泛型約束
- 子類和子類型
- 協變與逆變
- 泛型擦除與實化類型
- 泛型類型參數
- 泛型的背后:類型擦除
- Java為什么無法聲明一個泛型數組
- 向后兼容的罪
- 類型擦除的矛盾
- 使用內聯函數獲取泛型
- 打破泛型不變
- 一個支持協變的List
- 一個支持逆變的Comparator
- 協變和逆變
- 第三階段難點突破
- 注解和反射
- 聲明并應用注解
- DSL
- 協程
- 協程簡介
- 協程的基本操作
- 協程取消
- 管道
- 慕課霍丙乾協程筆記
- Kotlin與Java互操作
- 在Kotlin中調用Java
- 在Java中調用Kotlin
- Kotlin與Java中的操作對比
- 第四階段專題練習
- 朱凱Kotlin知識點總結
- Kotlin 基礎
- Kotlin 的變量、函數和類型
- Kotlin 里那些「不是那么寫的」
- Kotlin 里那些「更方便的」
- Kotlin 進階
- Kotlin 的泛型
- Kotlin 的高階函數、匿名函數和 Lambda 表達式
- Kotlin協程
- 初識
- 進階
- 深入
- Kotlin 擴展
- 會寫「18.dp」只是個入門——Kotlin 的擴展函數和擴展屬性(Extension Functions / Properties)
- Kotlin實戰-開發Android