Kotlin

Kotlin是一種在Java虛擬機上執行的靜態型別程式語言，它也可以被編譯成為JavaScript原始碼。

## 歷史

2011年7月，JetBrains在俄羅斯聖彼得堡的開發團隊推出Kotlin項目，其名稱來自於聖彼得堡附近的科特林島。2012年1月，著名期刊《Dr. Dobb's Journal》中Kotlin被認定為該月的最佳語言。這是一個面向JVM的新語言，它已被開發一年之久。JetBrains負責人Dmitry Jemerov說，大多數語言沒有他們正在尋找的特性，但是Scala除外。但是，他指出Scala的編譯時間過長這一明顯缺陷。Kotlin的既定目標之一是像Java一樣快速編譯。2012年2月，JetBrains以Apache 2許可證開源此項目。

JetBrains希望這個新語言能夠推動IntelliJ IDEA的銷售。

雖然與Java語法並不相容，但在JVM環境中Kotlin被設計成可以和Java程式碼相互運作，並可以重複使用如Java集合框架等的現有Java引用的函數庫。Hathibelagal寫道，「如果你正在為Android開發尋找一種替代程式語言，那麼應該試下Kotlin。它很容易在Android項目中替代Java或者同Java一起使用。」

Kotlin v1.0於2016年2月15日發布。這被認為是第一個官方穩定版本，並且JetBrains已準備從該版本開始的長期向後兼容性。

在Google I/O 2017中，Google宣布在Android上為Kotlin提供最佳支持。

## 語法

Kotlin很明顯受到Java、C#、JavaScript、Scala、Groovy等語言的影響。例如Kotlin可以直接通過`println("Hello, ${name}")`或`println("Hello, $name")`來使用字串模板，和古老的shell script類似。又如Kotlin中的分號是可選的，這類似JavaScript，而且Groovy、Scala也有同樣的能力。Kotlin常量定義關鍵字是`val`（不同於變量定義關鍵字`var`），表示固定值，這功能來自Scala，Swift也有類似功能。

需要注意，Kotlin沒有關鍵字`new`。

### 變量

使用`val`（全稱為value，即（固定的）值）關鍵字定義唯讀變量，定義後其值無法修改

```Kotlin
val a: Int = 1 // 定義a為`Int`類型的唯讀變量，其值為1
val b = 2 // 自動檢測b為`Int`類型
```

使用`var`（全稱為variable，即變量）關鍵字定義可變變量。

```Kotlin
var x = 5 // App 定義一個`Int`，值為5
x = 1 // 修改值為1
```

### 函數

使用`fun`關鍵字定義一個函數。

```Kotlin
fun sum(a: Int, b: Int): Int {
    return a + b
}
```

上例定義了一個傳入兩個Int變量，並返回兩數之和的求和函數。

### 程序的入口點

類似於 C、 C++、 C#、 Java 和 Go， Kotlin 程序的入口點是一個名為「main」的函數。main 函數有一個包含命令行選項的參數（從 Kotlin 1.3 開始是可選的）。Kotlin 支持像 Perl 和 Unix shell 那樣的字串模板。類型推斷也是支持的。

```kotlin
// Hello, world! 範例
fun main() {
    val scope = "World"
    println("Hello, $scope!")
}

fun main(args: Array<String>) {
    for (arg in args) {
        println(arg)
    }
}
```

### 函數擴展

Kotlin與C#、JavaScript類似，能夠擴展類別的新功能，而無需繼承該類別，或使用像裝飾器（decorator）這樣的任何類型的設計模式（design pattern）。擴展函數可以稱為Kotlin的核心，在標準庫里到處充斥著擴展函數。

擴展函數是靜態分發的，也就是說，它們不是以接收者類型為準的虛擬函數。這意味著調用的擴展函數是由函數調用所在的表達式的類型來決定的，而不是由表達式運行時求值結果決定的。

在下述例子中，String類別被擴展出一個成員lastChar。

```kotlin
package MyStringExtensions

fun String.lastChar(): Char = get(length - 1)

>>> println("Kotlin".lastChar()) 
```

利用函數擴展，Kotlin也支持運算符重載：

```kotlin
// overloading '+' operator using an extension method
operator fun Point.plus(other: Point): Point {
    return Point(x + other.x, y + other.y)
}

>>> val p1 = Point(10, 20)
>>> val p2 = Point(30, 40)
>>> println(p1 + p2)
Point(x=40, y=60)
```

### getter和setter

Kotlin像C#一樣支持屬性（property）。

### 解包引數

類似Python, 解包（unpack）指的是對實際參數的解包，只需在前面加一個星號* 即可，如test（*a）:

```kotlin
fun main(args: Array<String>) { 
    val list = listOf("args: ", *args)
    println(list)
}
```

### 函數嵌套

Kotlin支持函數嵌套（nested functions），允許函數內再定義函數，類似JavaScript、C#與Python語言。

```kotlin
class User(
    val id:      Int, 
    val name:    String, 
    val address: String) { 

    fun saveUser(user: User) {
       fun validate(user: User, value: String, fieldName: String) {
           if (value.isBlank()) {
               throw IllegalArgumentException(
                  "Can't save user ${user.id}: empty $fieldName")
           }
       }

       validate(user, user.name, "Name") 
       validate(user, user.address, "Address")
       // Save user to the database
    }
}
```

### 解構聲明

Kotlin支持解構聲明，這與Python的迭代解包相似。

例如, collection object 包含解構式可分離其元素:

```kotlin
for ((index, element) in collection.withIndex()) { 
     println("$index: $element")
}
```

### 抽象類別

抽象類別（Abstract classes）定義抽象或純虛擬（Pure Virtual）占位函數，需要被繼承。抽象類別預設是open的。

```kotlin
// No need for the open keyword here, its already open by default
abstract class Animated {

    // This virtual function is already open by default as well
    abstract fun animate()

    open fun stopAnimating() { }

    fun animateTwice() { }
}
```

### 類別屬性

Kotlin 提供下列的關鍵字來限制頂層（top-level）聲明，用於控制類別與成員在繼承時的可見性（作用域）。它們可用於類別及其成員:

```kotlin
public
internal
protected
private
```

用於類別的成員聲明時，含義如下：

- public：全局可見。此為默認的類型。
- internal：在當前模塊中可見。
- protected：在當前類別的一級子類別中可見，如果子類別再被繼承，則在下一級子類別中不可見。
- private：在當前類別中可見。

用於頂層聲明時，含義如下：

- public：全局可見。此為默認的類型。
- internal：在當前模塊中可見。
- private：在當前文件中可見。

例如:

```kotlin
// Class is visible only to current module
internal open class TalkativeButton : Focusable {
    // method is only visible to current class 
    private   fun yell() = println("Hey!") 

    // method is visible to current class and derived classes
    protected fun whisper() = println("Let's talk!")
}
```

### 主構造函數 vs. 二級構造函數

在Kotlin 中類別可以有一個主構造函數以及多個二級構造函數。如果主構造函數沒有註解或可見性說明，則`constructor`關鍵字可以省略。如果構造函數中沒有其它操作，大括號也可以省略。

```kotlin
// Example of class using primary constructor syntax
// (Only one constructor required for this class)
class User(
    val nickname: String, 
    val isSubscribed: Boolean = true) {
    ...
}
```

Kotlin 的二級構造函數更類似於 C++, C#, 和 Java。

```kotlin
// Example of class using secondary constructor syntax
// (more than one constructor required for this class)
class MyButton : View {

    // Constructor #1 
    constructor(ctx: Context) : super(ctx) { 
        // ... 
    } 

    // Constructor #2
    constructor(ctx: Context, attr: AttributeSet) : super(ctx, attr) { 
        // ... 
    }
}
```

### Anko library

Anko 是一組為Kotlin 打造的函數庫，其功能是用來開發Android UI 應用程式，隨著同類功能的Jetpack Compose發展，Anko現已棄用。

```kotlin
fun Activity.showAreYouSureAlert(process: () -> Unit) {
    alert(
      title   = "Are you sure?",
      message = "Are you really sure?") 
    {
      positiveButton("Yes") { process() }
      negativeButton("No") { cancel() }
    }
}
```

### Kotlin 交互模式

Kotlin除了編譯成Java字節碼運行，也可以作為腳本語言解釋運行，此特性使得Kotlin可以以交互模式運行。交互模式是腳本語言具有的特性，解釋器可以立即運行使用者輸入的代碼，並反饋運行結果。典型的語言有Python、JavaScript（在V8引擎支持下）、Ruby。

```bash
$ kotlinc-jvm
type :help for help; :quit for quit
>>> 2+2
4
>>> println("Welcome to the Kotlin Shell")
Welcome to the Kotlin Shell
>>>
```

### Kotlin 也是腳本語言

Kotlin 亦可視為腳本語言（scripting language）。其腳本存成 Kotlin source file (.kts)，即成為可執行檔。

```kotlin
// list_folders.kts
import java.io.File
val folders = File(args[0]).listFiles { file -> file.isDirectory() }
folders?.forEach { folder -> println(folder) }
```

為了執行Kotlin 腳本，我們在運行編譯器時再加上-script選項。

```bash
$ kotlinc -script list_folders.kts "path_to_folder_to_inspect"
```

### Kotlin 的 hello world 例子

```kotlin
fun main(args: Array<String>) {
    
    greet {
        to.place
    }.print()
}

//inline higher-order functions
inline fun greet(s: () -> String) : String = greeting andAnother s()  

//infix functions, extensions, type inference, nullable types, lambda expressions, labeled this, elvis operator
infix fun String.andAnother(other : Any?) = buildString() { append(this@andAnother); append(" "); append(other ?: "") } 

//immutable types, delegated properties, lazy initialization, string templates
val greeting by lazy { val doubleEl: String = "ll"; "he${doubleEl}o" }

//sealed classes, companion objects
sealed class to { companion object { val place = "world"} }

//extensions, Unit
fun String.print() = println(this)
```

### 空變量及其運算

Kotlin對可以為空（nullable）的變量和不可以為空（non-nullable）的變量作了區分。所有的可空對象（nullable objects）必須在定義時加上 "?" 後置於類型之後。開發人員遇到nullable objects時要先確認: null-check 須被執行過，才能賦值。可空性是Kotlin類型系統中幫助開發者避免以往Java的NullPointerException錯誤的特性。

Kotlin 提供空安全（null-safe）運算符給開發人員:

- (Safe navigation operator) 可用於安全存取（safely access）可能是空對象的函數或屬性。如果 object 為空（null）, 該方法將不被調用，而且表達式的值一定為空（null）。
- (Null coalescing operator) 通常稱為艾維斯運算符（Elvis operator）:

```kotlin
fun sayHello(maybe: String?, neverNull: Int) {
   // use of elvis operator
   val name: String = maybe ?: "stranger"
   println("Hello $name")
}
```

使用安全導引（safe navigation）運算符:

```kotlin
// returns null if...
// - foo() returns null,
// - or if foo() is non-null, but bar() returns null,
// - or if foo() and bar() are non-null, but baz() returns null.
// vice versa, return value is non-null if and only if foo(), bar() and baz() are non-null
foo()?.bar()?.baz()
```

### 高階函數與lambda

Kotlin 亦支持高階函數和lambdas功能。lambda是一種匿名函數，允許開發者直接將表達式定義為函數，這類似於Python。

```kotlin
// the following function takes a lambda, f, and executes f passing it the string, "lambda"
// note that (s: String) -> Unit indicates a lambda with a String parameter and Unit return type
fun executeLambda(f: (s: String) -> Unit) {
    f("lambda")
}
```

Lambdas 可用大括弧來定義。如果lambda 夾帶參數，他們可定義在大括弧內，並以運算符區隔。

```kotlin
// the following statement defines a lambda that takes a single parameter and passes it to the println function
val l = { c : Any? -> println(c) }
// lambdas with no parameters may simply be defined using { }
val l2 = { print("no parameters") }
```

## 基本資料

- 文件擴展名：.kt .kts
- 編程范型：多范型：面向對象、函數式、指令式、宣告式、泛型、反射式、並發
- 發行日期：2011
- 設計者：JetBrains
- 開發者：JetBrains與開源貢獻者
- 類型系統：靜態類型、類型推論
- 受影響於：Java、Scala、Groovy、C#、Gosu
- 平台：輸出Java虛擬機、Java字節碼以及JavaScript原始碼
- 作業系統：任何支援JVM或是JavaScript的直譯器
- 許可證：Apache 2