天天看點

《Kotin 程式設計思想·實戰》

《Kotin 程式設計思想·實戰》

1 JVM語言家族概覽

1.1 程式設計語言簡史

1.2 程式執行的三種方式

1.2.1 編譯執行

1.2.2 解釋執行

1.2.3 虛拟機執行

1.3 JVM概述

1.3.1 Java源代碼編譯執行過程

1.3.2 Java Class檔案簡介

1.3.3 JVM位元組碼執行過程

1.4 JVM語言家族

1.4.1 Scala

1.4.2 Groovy

1.4.3 Clojure

1.4.4 Kotlin

1.4.5 Xtend

Xtend是Eclipse推出的一個新的JVM語言,并無意替代Java,而是以己之長補Java之短,精簡代碼,無類型,改進可讀和維護。Eclipse Xtend可以編譯成可讀的Java代碼,類似CoffeeScript之于Javascript。

靜态類型

特點

擴充方法 :加強封閉類型的新功能。

Lambda表達式:匿名函數文字簡潔的文法。

将lambda表達式編譯成匿名内部類。

運算符重載:讓庫更表達。

強大的交換機的表達:類型與隐式類型轉換開關。

多個排程:即多态方法調用。

模闆表達式:智能空間處理。

報表:一切都是表達式。

屬性:通路和定義getter和setter方法的速記法。

局部類型推理:很少需要寫下類型簽名了。

全面支援Java的泛型:包括所有的一緻性和轉換規則。

類型系統:Xtend的靜态類型是不錯的,因為它允許更好的靜态分析和基于類型資訊的更好的工具。然而,缺點是額外的複雜性

2 Kotlin簡介

2.1 kotlin簡史

2.2 快速學習工具

2.2.1 雲端IDE

2.2.2 本地指令行環境搭建

2.2.3 Kotlin REPL

2.2.4 使用IntelliJ IDEA

2.2.5 使用Gradle建構工程

3 快速開始:HelloWorld

3.1 指令行的HelloWorld

3.2 應用程式版HelloWorld

3.3 Web RESTFul HelloWorld

3.4 Android版的HelloWorld

3.5 JavaScript(Canvas) 版HelloWorld

4 kotlinc編譯過程分析

4.1 kotlinc執行原理分析

4.2 kotlin代碼執行過程

4.3 Kt.class與Java.class差別

5 語言基礎

5.1 基礎文法

5.1.1 包(package)

package打包與import導包

源檔案可以不需要比對目錄和包,源檔案可以放在任何檔案目錄

如果沒有任何包聲明的話,則當中的代碼都屬于預設包,導入時包名即為函數名!

比如:import shortToast

另外你還可以在導入類的時候為類設定一個别名,比如:

import java.util.Date as d

直接在檔案中寫一堆fun方法!

kotlin中因為可以使用擴充方法,是以可以連class和interface都不寫,

5.1.2 變量

變量作用域

聲明變量

val

var

val定義常量和var定義變量,預設都是private的,比如

定義:val a =123, var b = 321,打開生成的.class檔案可以看到:

private私有,且預設寫了公有的getter和setter方法。

5.1.3 表達式

range

return

throw

三元表達式

Lambda表達式

this表達式

super表達式

5.1.4 代碼塊

5.1.5 分支控制流

if表達式

判斷結構(條件表達式)

Java int max = a>b?a:b

Kotlin: val max = if (a>b) a else b

在if語句塊的最後可以自動傳回最後一行表達式的值,而不需要寫return

fun ifExample(x: Int, y: Int) {

val result = if (x >= y) {

println("Condition ok.")

true

} else {

println("Condition else.")

false

}

println("Result $result")

}

when表達式

fun whenExample(userType: Int) {

when (userType) {

0 -> println("Registered user")

1 -> print("Administrator")

else -> {

println("Unknown")

}

}

}

fun whenExample2(userType: Int) {

when (userType) {

0, 1 -> println("Welcome user.")

else -> println("Permission denied.")

}

}

fun whenExample3(userType: Int) {

when (userType) {

filterUserType(userType) -> {

println("Subtype ok")

whenExample2(userType)

}

else -> print("Subtype not ok")

}

}

fun filterUserType(userType: Int): Int {

if (userType >= 0 && userType < 2) {

return userType;

}

return -1

}

fun whenExample4(x: Int) {

val from = 0

val to = 100

when (x) {

in from..to -> println("PRECISE")

in (from / 2)..(to / 2) -> print("VERY PRECISE")

50 -> print("STRAIGHT IN TARGET")

else -> print("MISSED")

}

}

fun whenExample5(fullName: String) {

val isJohn = when (fullName) {

is String -> fullName.startsWith("John ")

else -> false

}

}

fun whenExample6(fullName: String) {

when {

fullName.length == 0 -> println("Please enter your name.")

fullName.substring(0, 2).equals("X ") -> println("Hello Mr. X")

fullName.startsWith("John ") && !fullName.endsWith(" Smith") -> println("Hello John!")

fullName.endsWith(" Smith") -> println("Hello agent Smith.")

else -> println("Only secret agents allowed!")

}

}

5.1.6 循環

while循環

for循環

Kotlin中的while與do-while,break,continue與Java中的類似,不過Kotlin中多了個好玩的東西:

Ranages,包含與範圍有關的函數操作符

Ranages

在範圍内與不在範圍内

fun main(array: Array<String>) {

for ((index, value) in array.withIndex()) {

println("[ $index ][ $value ]")

}

val a1 = 1
val a2 = 2
val b1 = if(a1 in 0..9) true else false
val b2 = if(a2 !in 10..20 ) true else false
println(b1)
println(b2)

val str1 = "Hello"
val str2 = "Hello,Wolrd"
if(str1 in str2) println(str1 + " in " + str2) else println(str1 + " is not in " + str2)
if(str1 in "Hello".."Kotlin") println(str1 + " in " + "Hello".."Kotlin") else println(str1 + " is not in " + "Hello".."Kotlin")      

}

順序周遊

val arr = Array(10,{n->n})
arr.forEach(::print)
println()
arr.forEach{
    it->print(it.toString() + " ")
}
println()
for(e in arr) print(e)
println()
for(i in 0..arr.lastIndex) print(arr[i].toString() + " ")
println()      

你也可以調lastIndex來獲得最後的下标,寫成if(i in 0..array.lastIndex)

如果你不想順着周遊,想反過來周遊,可以利用downTo (遞減)關鍵字,從最大值到最小值遞減!

for(i in 9 downTo 5) print(arr[i])

println()

可能你還想隔着周遊,比如隻周遊:10,7,4,1,可以用 step (步長)關鍵字!後面跟着的是步長,

比如你可以寫成小數0.1這樣也行,示例:

for(i in 9 downTo 3 step 3) print(arr[i])

倒序周遊

5.1.7 代碼注釋

5.1.8 異常

Kotlin中所有的Exception都繼承了Throwable,含有一個message且未經檢查。

這表示不會強迫我們在任何地方使用try/catch,而Java中如果某個方法抛出

了Exception,就需要用try-catch包圍代碼塊。

Kotlin抛出異常和try-catch-finally和Java中的類似!但是Kotlin中throw和try都是表達式,

意味着他們可以指派給某個變量,這一點在處理邊界問題的時候很有用!代碼示例:

class ExceptionExamples {

fun exceptionExmple() {
    try {
        // do something ...
    } catch (e: KotlinNullPointerException) {
        // handle exception
    } finally {
        // do something ...
    }
}

// Try / Catch is expression!
fun exceptionExample2(): Int {
    return try {
        // do something
        0
    } catch (e: KotlinNullPointerException) {
        // handle exception
        -1
    }
}      

}

5.2 辨別符

5.2.1 修飾符

通路權限

public:預設,總是可見

internal:同子產品可見

private:聲明範圍與同子產品的子作用域可見

protected:類似于private,但對子類也可見

5.2.2 關鍵保留字

var:定義變量

val:定義常量

fun:定義方法

Unit:預設方法傳回值,類似于Java中的void,可以了解成傳回沒什麼用的值

vararg:可變參數

如果是可變參數的話,可以使用 vararg 關鍵字修飾

fun sum(vararg args: Int) {

var sum = 0

for (x in args) {

sum += x

}

println("Sum: $sum")

}

fun trySum(){

sum(1, 3, 6, 10, 1, 2, 3, 4)

}

$:字元串模闆(取值)

位運算符:or(按位或),and(按位與),shl(有符号左移),shr(有符号右移),

ushr(無符号右移),xor(按位異或),inv(按位取反)

in:在某個範圍中

downTo:遞減,循環時可用,每次減1

step:步長,循環時可用,設定每次循環的增加或減少的量

when:Kotlin中增強版的switch,可以比對值,範圍,類型與參數

is:判斷類型用,類似于Java中的instanceof()

5.2.3 運算符

5.2.4 指派符

5.3 函數

5.3.1 main函數

5.3.2 定義函數

使用 fun 關鍵字來聲明

如果沒有通路控制符修飾的fun預設是public final的!

傳回值:Unit

擴充函數

直接定義在檔案中,而不需要依賴于任何的類的函數

成員函數

寫在class或object中的函數

5.3.3 包級函數

5.3.4 Lambda表達式

// lambda寫法1

val runnable3 = Runnable { ->

println("I'm a Lambda")

}

// lambda寫法2

val runnable4 = { println("I'm a Lambda") }

Thread(runnable4).start()

函數式接口(functional interface)

隻包含一個抽象方法的接口

Java标準庫中的java.lang.Runnable和java.util.Comparator

public void runThread() {

new Thread(new Runnable() {

public void run() {

System.out.println("Run!");

}

}).start();

}

public void runThreadUseLambda() {

new Thread(() -> {

System.out.println("Run!");

}).start();

}

Collections.sort(list, (x, y) -> y - x);

List input = Arrays.asList(new String[] {"apple", "orange", "pear"});

input.forEach((v) -> System.out.println(v));

input.forEach(System.out::println);

5.3.5 閉包

5.3.6 匿名函數

// new 一個線程

// 匿名類寫法

val runnable1 = object : Runnable{

override fun run() {

println("I'm an anonymous class")

}

}

// 函數寫法, 略像js

val runnable2 = fun (){

println("I'm a function")

}

5.4 特色功能

5.4.1 函數拓展和屬性拓展(Extensions)

fun main(args: Array<String>) {

val list = listOf("1", "2", "3", "4")

// 函數拓展
list.myForEach { println(it) }

// 屬性拓展
println("last: ${list.lastItem}")      

}

/**

  • 拓展 List 類, 加一個自定義的周遊方法

    */

    fun <T> List<T>.myForEach(doTask: (T) -> Unit){

    for(item in this)

    doTask(item)

    }

/**

  • 拓展 List 類, 加一個自定義的長度屬性

    */

    val <T> List<T>.lastItem: T

    get() = get(size - 1)

// 輸出:

1

2

3

4

last: 4

class Employee {

fun name(): String {

return "Employee name"

}

}

class ExtendedFunctionalityExample(val e: Employee) {

// We extended Employee class with function that does not exist in original class!

fun Employee.age(): Int {

return 25

}

fun tryExtendedEmployeeExample() {
    println("Name: ${e.name()}, Age: ${e.age()}")
}      

}

5.4.2 屬性代理

以懶加載為例,lazySum可能需要複雜的運算,我們把它代理給lazy。 可以看到,隻有第一次加載進行了計算,之後都是直接取值,提高了效率。

val lazySum: Int by lazy {

println("begin compute lazySum ...")

var sum = 0

for (i in 0..100)

sum += i

println("lazySum computed!\n")

sum // 傳回計算結果

}

fun main(args: Array<String>) {

println(lazySum)

println(lazySum)

}

// 輸出:

begin compute lazySum ...

lazySum computed!

5050

5050

5.4.3 委托(Delegate)

5.4.4 空指針安全

空對象檢查Null Check

var mNullable: String? = null

var mNonNull: String = "mNonNull"

fun testNull(){

println("testNull: ")

println(mNullable?.length)

println(mNonNull.length)

println()

}

// 輸出:

testNull:

null

8

// java 風格,判空

if(mNullable != null)

mNullable.length

// kotlin 文法糖,判空(推薦)

mNullable?.length

null check實作原理簡析

空類型強轉為非空類型

var user: User? = getUser()

user!!.name = "Jack"

5.4.5 Lazy Evaluation

class UsesLazy {

val myLazyValue: String by lazy {

println("I am initializing this lazy value!")

"My lazy value!"

}

}

fun useLazy(){

val usesLazy: UsesLazy = UsesLazy()

val a: String = usesLazy.myLazyValue

val b: String = usesLazy.myLazyValue

val c: String = usesLazy.myLazyValue

}

6 類型系統

6.1 編譯時類型與運作時類型

6.2 根類型Any

對象相等性

6.3 基本類型(Primitive Types)

6.3.1 Number: 包含整型與浮點型等

kotlin.Byte

kotlin.Short

kotlin.Int

kotlin.Long

kotlin.Float

kotlin.Double

6.3.2 Char: 字元類型(Character)

6.3.3 Boolean: 布爾類型

6.3.4 String: 字元串類型

字元串常量

字元串函數

字元串模闆

轉義字元串

Kotlin居然沒有自動轉型

for(i in 0..arr.lastIndex) print(arr[i] + " ") 不能自動轉型,這樣寫代碼多麻煩

for(i in 0..arr.lastIndex) print(arr[i].toString() + " ")

6.3.5 Array: 數組類型

原生數組類型

建立數組

定長數組:val fixedSizeArray = arrayOfNulls<Int>(10)

空數組: val empty = emptyArray<Int>()

裝箱操作:val arr = arrayOf(1, 2, 3) //還有其他比如IntArrayOf,BooleanArrayOf等

閉包初始化:

val arr = Array(100, {num -> num})
for(i in 0..99) println(arr[i])      

通路數組

使用[]

[]通路數組元素在這裡實際上是進行了操作符的

重載,調用的其實是Array類的getter和setter方法,但是編譯成位元組碼的時候會進行優化,

變成直接通路數組的記憶體位址,是以并不會造成性能損失!

周遊數組

foreach周遊

for(e in arr) println(e)

根據下标周遊

for(i in arr.indices) println(arr[i])

indices代表下标!範圍:(0 <= indices < 數組size)

6.4 特殊類型

kotlin.Any

kotlin.Nothing

kotlin.Unit

kotlin.KClass<T>

6.5 可空類型(Nullable Types)

6.6 函數類型( Functional Types)

閉包類型

6.7 類型檢測

is運算符

as運算符

6.8 類型轉換

6.9 類型别名typealias

6.10 泛型

fun <T> genericFunctionsExample(x: T){

println("Value: $x")

}

fun tryGenericFunctionsExampe(){

genericFunctionsExample(5)

genericFunctionsExample("Some word!")

genericFunctionsExample('c')

genericFunctionsExample(5.55)

}

7 面向對象程式設計(OOP)

7.1 面向對象思想

7.2 類與繼承

7.2.1 類

類的橫向分類

抽象類

接口類

枚舉類

注解類

靜态類與伴生對象

sealed 密封類

sealed class SuperEnum {

class Human(val race: String) : SuperEnum()

class Animal(val specie: String, val legsCount: Int) : SuperEnum()

object Bacteria : SuperEnum()

}

fun trySuperEnum(superEnum: SuperEnum): String = when (superEnum) {

is SuperEnum.Human -> "Human ${superEnum.race}"

is SuperEnum.Animal -> "${superEnum.specie} with ${superEnum.legsCount} legs."

is SuperEnum.Bacteria -> "Some micro organism ..."

}

data 資料類

data class Person(val name: String, val age: Int){}

fun tryDataClassCopying(){

val p = Person("John Smith", 1985)

val p2 = p.copy()

val p3 = p.copy(age = 1990)

val p4 = p.copy("John Doe")

println(p)
println(p2)
println(p3)
println(p4)      

}

data class Employee(val name: String, val age: Int) {}

data class Worker(val name: String = "Unknown", val age: Int = 1970) {}

類的縱向組合

嵌套類Nested Class

内部類Inner Class

匿名内部類Inner Class

聲明類

類修飾符

構造函數

主構造函數

次構造函數

類的屬性(資料結構)

類的行為(算法函數)

7.2.2 接口與抽象類

接口的預設實作

interface A {

fun foo() { println("A") } // 預設實作, 列印"A"

fun bar()

}

interface B {

fun foo() { println("B") }

fun bar() { println("bar") }

}

// 多繼承時,顯式指定 super<A>.foo() 以去沖突

class D : A, B {

override fun foo() {

super<A>.foo()

super<B>.foo()

}

override fun bar() {
    super.bar()
}      

}

考慮下面的一個簡單的進行貨币轉換的接口。該接口的實作方式可能是調用第三方提供的服務來完成實際的轉換操作。

public interface CurrencyConverter {

BigDecimal convert(Currency from, Currency to, BigDecimal amount);

}

該接口在開發出來之後,在應用中得到了使用。在後續的版本更新中,第三方服務提供了新的批量處理的功能,允許在一次請求中同時轉換多個數值。最直接的做法是在原有的接口中添加一個新的方法來支援批量處理,不過這樣會造成已有的代碼無法運作。而預設方法則可以很好的解決這個問題。使用預設方法的新接口如下所示。

public interface CurrencyConverter {

BigDecimal convert(Currency from, Currency to, BigDecimal amount);

default List convert(Currency from, Currency to, List amounts) {
    List result = new ArrayList();
        for (BigDecimal amount : amounts) {
            result.add(convert(from, to, amount));
        }
        return result;
}      

}

新添加的方法使用default關鍵詞來修飾,并可以有自己的方法體。

目的

接口的預設方法的主要目标之一是解決接口的演化問題。當往一個接口中添加新的方法時,可以提供該方法的預設實作。對于已有的接口使用者來說,代碼可以繼續運作。新的代碼則可以使用該方法,也可以覆寫預設的實作。

實作行為的多繼承

7.2.3 繼承

open類

7.2.4 實作接口

7.2.5 函數重載

override重寫覆寫父類函數

7.3 類的執行個體對象

建立對象

對象屬性(資料結構)

對象行為(算法函數)

7.4 委托

類的委托

屬性的委托

8 函數式程式設計(FP)

8.1 函數式程式設計概述

面向對象程式設計OOP特征

函數式程式設計FP特征

8.2 Kotlin函數式程式設計

8.2.1 函數是什麼

内聯函數

8.2.2 函數指針

8.2.3 複合函數(高階函數)

8.2.4 閉包(closure)

js閉包

function closureExample(objID, text, timedelay) {

setTimeout(function() {

document.getElementById(objID).innerHTML = text;

}, timedelay);

}

closureExample(‘myDiv’, ‘Closure is created’, 500);

groovy閉包

Kotlin閉包

val test = if (5 > 3) {

print("yes")

} else {

print("no")

}

fun tryClosures() {

val values = listOf<Int>(2, 4, 6, 8, 10)
var result = 0
values.forEach {
    result += it
}
println("Result: $result")      

}

函數、Lambda、if語句、for、when,都可以稱之為閉包

自執行閉包

自執行閉包就是在定義閉包的同時直接執行閉包,一般用于初始化上下文環境。 例如:

{ x: Int, y: Int ->

println("${x + y}")

}(1, 3)

節制的自由

使用閉包寫代碼相當靈活自由,省略了很多的臨時變量和參數聲明。 然而,也正是因為閉包的靈活性,造成如果泛濫的話,可能會寫出可讀性非常差的代碼。

8.2.5 Lambda表達式(匿名函數)

Lambda 表達式俗稱匿名函數,熟悉Java的大家應該也明白這是個什麼概念。Kotlin 的 Lambda表達式更“純粹”一點, 因為它是真正把Lambda抽象為了一種類型,而 Java 8 的 Lambda 隻是單方法匿名接口實作的文法糖罷了。

val printMsg = { msg: String ->

println(msg)

}

fun main(args: Array<String>) {

printMsg.invoke("hello")

}

以上是 Lambda 表達式最簡單的執行個體。

首先聲明了一個名為 printMsg 的 Lambda,它接受一個 String 類型的值作為參數,然後在 main 函數中調用它。如果還想省略,你還可以在調用時直接省略invoke,像函數一樣使用。

fun main(args: Array<String>) {

printMsg("hello")

}

Lambda 表達式作為高階函數的參數傳遞

fun main(args: Array<String>) {

log("world", printMsg)

}

val printMsg = { str: String ->

println(str)

}

val log = { str: String, printLog: (String) -> Unit ->

printLog(str)

}

這個例子中,log 是一個接受一個 String 和一個以 String 為參數并傳回 Unit 的 Lambda 表達式為參數的 Lambda 表達式。

8.2.6 内聯函數(inline)

使用 高階函數 在運作時會帶來一些不利: 每個函數都是一個對象, 而且它還要捕獲一個閉包, 也就是, 在函 數體内部通路的那些外層變量. 記憶體占用(函數對象和類都會占用記憶體) 以及虛方法調用都會帶來運作時的消耗.

但是也不是說所有的函數都要内聯,因為一旦添加了 inline 修飾,在編譯階段,編譯器将會把函數拆分,插入到調用出。如果一個 inline 函數是很大的,那他會大幅增加調用它的那個函數的體積。

infix fun Double.powerPI(x: Int): Double {

return Math.pow(this, x + Math.PI)

}

fun infixExample() {

val array = arrayOf(2.0, 4.0, 6.0, 8.0, 10.0)

for (value in array) {

val result = value powerPI 5

println("Result: [ $value ][ $result ]")

}

}

8.2.7 本地函數(Local Functions)

fun worker() {

fun doWork(work: String) {

println("Working [ $work ]")

}

doWork("Importing user data")
doWork("Processing user data")
doWork("Exporting user data")      

}

8.2.8 命名參數(NamedParameters)

fun addEmployee(name: String, year: Int, address: String) {

}

// Let's call method:

fun namedParametersExaple() {

addEmployee(

name = "John Smith",

year = 1985,

address = "Avenue 666"

)

}

8.2.9 外部函數external

8.2.10 尾遞歸tailrec

tailrec fun tailRecursiveExample(word: String) {

if (word.length == 1) {

println("--- end")

} else {

println(word)

tailRecursiveExample(word.substring(0..(word.length - 2)))

}

}

fun tryTailRecursiveExample() {

tailRecursiveExample("My word")

}

8.3 函數式Stream API

8.3.1 filter函數

8.3.2 map函數

fun main(args: Array<String>){

val list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

list.filter { it%2==0 } // 取偶數

.map{ it*it } // 平方

.sortedDescending() // 降序排序

.take(3) // 取前 3 個

.forEach { println(it) } // 周遊, 列印

}

8.3.3 forEach

9 Kotlin與Java互操作(Interoperability)

9.1 使用工具互相轉換

9.1.1 将 Java 轉換為 Kotlin

9.1.2 将 Kotlin 轉換為 Java

9.1.3 用 Kotlin 相容 Java 的做法,本來就是權宜之計,相容必然帶來新舊兩種觀念的沖突以及醜陋的發生

9.2 Kotlin與Java互操作

9.2.1 Kotlin無縫調用第三方jar庫

9.2.2 執行shell

9.2.3 檔案操作

9.2.4 多線程代碼

9.3 Kotlin與Java的差別

9.3.1 void 與 Unit

9.3.2 反射擷取類的 Class

9.3.3 Java 與 Kotlin 關鍵字沖突的處理

9.3.4 static 方法與伴生對象companion object

9.3.5 包級别函數

9.3.6 重載必須使用override

10 集合類與泛型

10.1 kotlin.collections

10.1.1 不可變集合類

List

// Gives us read only list

fun getList(): List<Int> {

return listOf(1, 3, 5, 7)

}

// Gives us read only map

fun getMap(): Map<String, Double> {

return mapOf("EUR" to 123.20, "USD" to 110.34, "CHF" to 111.4)

}

fun printMap(key: String) {

val map = getMap()

if (key in map) println(map[key]) else println("There is no such key $key")

}

Set

Map

fun traverseMapExample(map: Map<String, Int>) {

for ((key, value) in map) println("$key : $value")

}

10.1.2 可變集合類

kotlin.collections.MutableCollection<E>

MutableList<E>

MutableSet<E>

MutableMap<K, V>

10.2 泛型與類型安全

10.2.1 類型參數

10.2.2 類型推測

10.2.3 協變與逆變

10.3 類型上下界

11 輕量級線程:協程(Coroutines)

11.1 協程概念

val fibonacci = buildSequence {

yield(1) // first Fibonacci number

var cur = 1

var next = 1

while (true) {

yield(next) // next Fibonacci number

val tmp = cur + next

cur = next

next = tmp

}

}

...

for (i in fibonacci){

println(i)

if(i > 100) break //大于100就停止循環

}

用看似同步的代碼做着異步的事情

11.2 協程的基本操作

11.2.1 建立

11.2.2 啟動

11.2.3 暫停

11.2.4 繼續

11.3 競争條件

11.4 同步

11.5 實作異步

12 使用Kotlin開發Web應用

12.1 Kotlin內建Spring Boot開發Web應用

12.1.1 Spring Boot簡介

12.1.2 Kotlin內建Spring Boot

12.2 Spring 5 對 Kotlin的支援

12.2.1 Functional bean registration with Kotlin

Spring Framework 5.0 introduces a new way to register beans using lambda as an alternative to XML or JavaConfig with @Configuration and @Bean. In a nutshell, it makes it possible to register beans with a Supplier lambda that acts as a FactoryBean.

In Java you will for example write:

GenericApplicationContext context = new GenericApplicationContext();

context.registerBean(Foo.class);

context.registerBean(Bar.class, () -> new

Bar(context.getBean(Foo.class))

);

While in Kotlin, reified type parameters allow us to simply write:

val context = GenericApplicationContext {

registerBean<Foo>()

registerBean { Bar(it.getBean<Foo>()) }

}

12.2.2 使用Kotlin的函數式風格API開發 Web應用

Spring Framework 5.0 comes with a Kotlin routing DSL that allows you to leverage the Spring Functional Web API recently announced with clean and idiomatic Kotlin code:

{

("/blog" and accept(TEXT_HTML)).route {

GET("/", this@BlogController::findAllView)

GET("/{slug}", this@BlogController::findOneView)

}

("/api/blog" and accept(APPLICATION_JSON)).route {

GET("/", this@BlogController::findAll)

GET("/{id}", this@BlogController::findOne)

}

}

​​https://github.com/EasyKotlin/mixit​​

12.2.3 Kotlin Script based templates

ScriptTemplateView to render templates

This could allow you to write this kind of templates with full autocompletion and refactoring support in your IDE:

import io.spring.demo.*

"""

${include("header")}

<h1>${i18n("title")}</h1>

<ul>

${users.joinToLine{ "<li>${i18n("user")} ${it.firstname} ${it.lastname}</li>" }}

</ul>

${include("footer")}

"""

​​https://github.com/EasyKotlin/kotlin-script-templating​​

12.3 使用Kotlin的Web架構Ktor開發Web應用

Ktor is a framework for quickly creating web applications in Kotlin with minimal effort.

import org.jetbrains.ktor.netty.*

import org.jetbrains.ktor.routing.*

import org.jetbrains.ktor.application.*

import org.jetbrains.ktor.host.*

import org.jetbrains.ktor.http.*

import org.jetbrains.ktor.response.*

fun main(args: Array<String>) {

embeddedServer(Netty, 8080) {

routing {

get("/") {

call.respondText("Hello, world!", ContentType.Text.Html)

}

}

}.start(wait = true)

}

​​https://github.com/Kotlin/ktor/wiki​​

12.4 基于Kotlin Script的模闆引擎

13 使用Kotlin實作DSL

13.1 DSL

13.2 Groovy的DSL文法

13.3 Kotlin使用閉包建構 DSL

14使用Kotlin開 發JavaScript代碼

14.1 Kotlin代碼編譯成js過程

14.2 使用Kotlin開發JavaScript代碼

15 使用Kotlin開發Android程式

​​https://github.com/Kotlin/anko​​

class MyActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {

super.onCreate(savedInstanceState, persistentState)

MyActivityUI().setContentView(this)

}

}

class MyActivityUI : AnkoComponent<MyActivity> {

override fun createView(ui: AnkoContext<MyActivity>) = ui.apply {

verticalLayout {

editText()

button("Say Hello") {

onClick { ctx.toast("Hello!") }

}

}

}.view

}

16 使用Kotlin Native開發原生應用

17 KOTLIN語言生态

17.1 測試(Testing)

17.2 依賴注入(Dependency Injection)

17.3 JSON序列化(JSON serialization)

17.4 Web 架構

17.5 資料庫通路(Database access)

17.6 工具類(Utilities)

17.7 桌面程式設計(Desktop programming)

17.8 Http庫

Fuel: ​​https://github.com/EasyKotlin/Fuel​​

//an extension over string (support GET, PUT, POST, DELETE with httpGet(), httpPut(), httpPost(), httpDelete())

"​​​http://httpbin.org/get​​​".httpGet().responseString { request, response, result ->

//do something with response

when (result) {

is Result.Failure -> {

error = result.getAs()

}

is Result.Success -> {

data = result.getAs()

}

}

}

//if we set baseURL beforehand, simply use relativePath

FuelManager.instance.basePath = "​​​http://httpbin.org​​​"

"/get".httpGet().responseString { request, response, result ->

//make a GET to ​​​http://httpbin.org/get​​​ and do something with response

val (data, error) = result

if (error != null) {

//do something when success

} else {

//error handling

}

}

//if you prefer this a little longer way, you can always do

//get

Fuel.get("​​​http://httpbin.org/get​​​").responseString { request, response, result ->

//do something with response

result.fold({ d ->

//do something with data

}, { err ->

//do something with error

})

}

val (request, response, result) = "​​http://httpbin.org/get​​".httpGet().responseString() // result is Result<String, FuelError>

17.9 并發庫kotlinx.coroutines

kotlinx.coroutines:​​https://github.com/EasyKotlin/kotlinx.coroutines​​

​​https://github.com/EasyKotlin/kotlinx.coroutines/blob/master/coroutines-guide.md​​

fun main(args: Array<String>) {

launch(CommonPool) { // create new coroutine in common thread pool

delay(1000L) // non-blocking delay for 1 second (default time unit is ms)

println("World!") // print after delay

}

println("Hello,") // main function continues while coroutine is delayed

Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive

}

Run this code:

Hello,

World!

18 附錄 參考資料

​​https://github.com/EasyKotlin/kotlin-in-action​​

Awesome Kotlin: ​​https://kotlin.link/​​

Kotlin項目Github源碼:​​https://github.com/JetBrains/kotlin​​

Kotlin語言規範:​​http://jetbrains.github.io/kotlin-spec/​​

線上體驗學習Kotlin語言:​​https://try.kotlinlang.org​​

官網文檔:​​http://kotlinlang.org/docs/​​

​​https://github.com/trending?l=kotlin​​

​​https://github.com/EasyKotlin/Kotlin-for-Android-Developers​​