Kotlin學習筆記

這份筆記是我在閱讀kotlin官方文檔的時候整理記錄的
轉載請標明出處: http://blog.csdn.net/zull_kos...


1.常識

  • private >>> 只在該類(以及它的成員)中可見
  • protected >>> 類和它的子類可見
  • internal >>> 所在的整個 module 可見。
  • public >>> 任何地方可見

    (一個定義爲 public 的成員被包含在一個 private 修飾的類中,這個成員在這個類之外也是不可見的。)html

/**
 * 1.包名:
 */
//沒必要和文件夾路徑一致:源文件能夠放在任意位置。
package grammar

fun main(args: Array<String>) {
    println("2.1-->" + sum(4, 6))   //2.定義函數
    println("2.2-->" + sum(3, 4, 5))
    printSum(-1, 8)
}
/**
 * 2.定義函數
 * */
//2.1定義一個函數接受兩個 int 型參數,返回值爲 int :
fun sum(a: Int, b: Int): Int {
    return a + b
}

//2.2該函數只有一個表達式函數體以及一個自推導型的返回值:
fun sum(a: Int, b: Int, c: Int) = a + b + c

//2.3返回一個沒有意義的值:
fun printSum(a: Int, b: Int): Unit {
    println("結果: $a + $b = ${a + b}")
}

//2.3-Unit 的返回類型能夠省略:(此方法同上 ": Unit"能夠省略
fun printSumCopy(a: Int, b: Int) {
    println("sum of $a and $b is ${a + b}")
}

1.1 可使用下劃線增長數值常量的可讀性

val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

1.2 相等

=== 徹底相等(包括物理地址java

== 值相等node

1.3 三引號:"""

//多行string是由三個引號包裹的("""),不能夠包含分割符但能夠包含其它字符,能夠經過trim-margin()函數移除空格
val text = """
    |Tell me and I forget.
    |Teach me and I remember.
    |Involve me and I learn.
    |(Benjamin Franklin)
    """.trimMargin()

2.變量

package grammar

/*
Kotlin 字符串中,$符號後面能夠跟變量和函數
var a = 55
var b = 88
println("a+b=${a+b}")*/

/*
多行輸入符 三個雙引號
三引號的形式用來輸入多行文本,
也就是說在三引號之間輸入的內容將被原樣保留,
之中的單號和雙引號不用轉義,
其中的不可見字符好比/n和/t都會被保留
""" \n"""
*/

/**
 * 1.變量
 */
fun variab
le() {
    //
  al a: Int = 1  // 當即初始化
    val b = 2   // 推導出Int型
    val c: Int  // 當沒有初始化值時必須聲明類型
    c = 3       // 賦值
    println("a = $a, b = $b, c = $c")

    //----------------------------------------------------------
    //變量
    var y: Int = 5
    var x = 5 // 推導出Int類型
    x += 1
    var z: Int
    z = 9
    println("x = $x,y=$y,z=$z")
}

/**
 * 2.字符串模板
 * */
fun txt() {
    var a = 1
    // 使用變量名做爲模板:
    val s1 = "a is $a"

    a = 2
    // 使用表達式做爲模板:
    val s2 = "${s1.replace("is", "was")}, but now is $a"
    println(s2)
}

/**
 * 3.使用條件表達式
 * */
fun maxOf(a: Int, b: Int): Int {
    if (a > b) {
        return a
    } else {
        return b
    }
}

/**
 *4.使用可空變量以及空值檢查
 * 當空值可能出現時應該明確指出該引用可空。
 * 下面的函數是當 str 中不包含整數時返回空:
 * */
fun printProduct(arg1: String, arg2: String) {
    val x = parseInt(arg1)
    val y = parseInt(arg2)
    // 直接使用 x*y 會產生錯誤由於它們中有可能會有空值
    if (x != null && y != null) {
        // x 和 y 將會在空值檢測後自動轉換爲非空值
        println(x * y)
    } else {
        println("Error: '$arg1' 或者 '$arg2' 中間有的不是數字")
    }
}

fun parseInt(str: String): Int? {
    return str.toIntOrNull()
}

/**
 * 5.使用值檢查並自動轉換
 * */
//使用 is 操做符檢查一個表達式是不是某個類型的實例。若是對不可變的局部變量或屬性進行過了類型檢查,就沒有必要明確轉換:
fun printLength(obj: Any) {
    println("'$obj' 字符串的長度是 ${getStringLength(obj) ?: "Error:不是字符串"} ")
}

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // obj 將會在這個分支中自動轉換爲 String 類型
        return obj.length
    }

    // obj 在種類檢查外仍然是 Any 類型
    return null
}

3.Kotlin使用習慣

3.1 關鍵字in

使用 in 操做符檢查數值是否在某個範圍內git

fun inTxt(x: Int) {
    if (x in 1..10) {
        println("$x 在範圍1-10之間")
    } else {
        println("$x 超出範圍")
    }
}

3.2 Ranges(迭代和步進)

private fun diedai() {
    print("在範圍內迭代:")
    for (u in 1..5) {
        print(u)
    }
}

//步進:循環中遞增的倍數
private fun diedai2() {
    print("\n使用步進:")
    for (x in 1..10 step 2) {
        print(x)
    }
    for (x in 9 downTo 0 step 3) {
        print(x)
    }
}
//downTo()函數:倒序迭代//print(i)
private fun RangesTxt() {
    for (i in 1..10)  //至關於 of 1 <= i && i <= 10
    for (i in 1 until 10) // i in [1, 10), 不包含:10  
    for (i in 1..4) print(i) // prints "1234"
    for (i in 4..1) print(i) // prints nothing
    for (i in 4 downTo 1) print(i) // prints "4321"
    for (i in 1..4 step 2) print(i) // prints "13"
    for (i in 4 downTo 1 step 2) print(i) // prints "42"
    //step()--能夠進行任意數量的迭代,而不是每次變化都是1
    for (i in 1..4 step 2) print(i) // prints "13"--遍歷每次自增2
    for (i in 4 downTo 1 step 2) print(i) // prints "42
}

3.3 集合

private fun ListTxt() {
    val list1 = listOf("vdf", "vdrf", "vsdf")
    val list2 = setOf("vdf", "vdrf", "vsdf")
    when {
    //使用 in 操做符檢查集合中是否包含某個對象
        "vsd" in list1 -> print("cvds")
    }

    //使用lambda表達式過濾和映射集合:
    list1.filter { it.startsWith("a") }
            .sortedBy { it }
            .map { it.toUpperCase() }
            .forEach { print(it) }
}

3.3.1 只讀集合

val list = listOf("a", "b", "c") //只讀 list
val map = mapOf("a" to 1, "b" to 2, "c" to 3) //只讀map

3.4 if帶返回值的

fun foo(param: Int){
    val result = if (param == 1) {
        "one"    //結果:result="one"
    } else if (param == 2) {
        "two"    //結果:result="two"
    } else {
        "three"    //結果:result="three"
    }
}

3.5 函數的默認值

fun foo(a: Int = 0, b: String = "") {...}
//注:至關於java的重載
void foo(int a){}
void foo(int a,String b)

3.6 只有一個表達式的函數

fun theAnswer() = 42
//至關於下面的寫法
fun theAnswer(): Int {
    return 42
}
//這個能夠和其它習慣用語組合成高效簡潔的代碼。譬如說 when 表達式:
fun transform(color: String): Int = when (color) {
        "Red" -> 0
        "Green" -> 1
        "Blue" -> 2
        else -> throw IllegalArgumentException("Invalid color param value")
    }

3.7 產生一個可能爲空的布爾值

val b: Boolean? = ...
if (b == true) {
  ...
} else {
  // `b` 是false或者null
}

3.8 一個函數返回倆個值

  • 要是一個函數想返回倆個值。好比,一個對象結果,一個是排序的狀態。在 Kotlin 中的一個緊湊的方案是聲明 data 類並返回實例:算法

    data class Result(val result: Int, val status: Status)
    
    fun function(...): Result {
        //...
        return Result(result, status)
    }
    
    val (result, status) = function(...)

3.9 等式

  • 參照相等

參照相等是經過 === 操做符判斷的(不等是!== ) a===b 只有 a b 指向同一個對象是判別才成立。數據庫

另外,你可使用內聯函數 identityEquals() 判斷參照相等:json

a.identityEquals(b)
a identityEquals b

  • 結構相等

結構相等是經過 == 判斷的。像 a == b 將會翻譯成:數組

a?.equals(b) ?: b === null

若是 a 不是 null 則調用 equals(Any?) 函數,不然檢查 b 是否參照等於 null安全

注意徹底沒有必要爲優化你的代碼而將 a == null 寫成 a === null 編譯器會自動幫你作的。多線程

4.流程控制

4.1 循環

//for:  經過任何提供的迭代器進行迭代。語法是下面這樣的:
fun loopFor() {
    val items = listOf("apple", "banana", "kiwi")
    for (str in items) {
    }
    for (index in items.indices) {
        println("第 $index 個是 ${items[index]}")
    }
    for (index in 0..viewGroup.getChildCount() - 1) {
          val view = viewGroup.getChildAt(index)
          view.visibility = View.VISIBLE
    }
}
//while
fun loopWhile() {
    val items = listOf("apple", "banana", "kiwi")
    var index = 0
    while (index < items.size) {
        println("第 $index 個是 ${items[index]}")
        index++
    }
  //do...while
  do {
      val y = retrieveData()
    } while (y != null){ // y 在這是可見的
    }

  //遍歷 map/list
  for ((k, v) in map) {
      print("$k -> $v")
  }

4.4.1 循環與集合

//使用 in 運算符來判斷集合內是否包含某實例:
fun main(args: Array<String>) {
    val items = setOf("apple", "banana", "kiwi")
    when {
        "orange" in items -> println("juicy")
        "apple" in items -> println("apple is fine too")
    }
}
//使用 lambda 表達式來過濾(filter)和映射(map)集合:
fun main(args: Array<String>) {
    val fruits = listOf("banana", "avocado", "apple", "kiwi")
    fruits
        .filter { it.startsWith("a") }
        .sortedBy { it }
        .map { it.toUpperCase() }
        .forEach { println(it) }
}

4.2 when 表達式

  • when會對全部的分支進行檢查直到有一個條件知足。when 能夠用作表達式或聲明。若是用做表達式的話,那麼知足條件的分支就是總表達式。若是用作聲明,那麼分支的值會被忽略。(像 if 表達式同樣,每一個分支是一個語句塊,並且它的值就是最後一個表達式的值)
  • 在其它分支都不匹配的時候默認匹配 else 分支。若是把 when 作爲表達式的話 else 分支是強制的,除非編譯器能夠證實分支條件已經覆蓋全部可能性。
//注意,因爲 smart casts ,你能夠不用另外的檢查就可使用相應的屬性或方法。
fun WhenTxt(obj: Any): String {
    return when (obj) {
        1,0         -> "One or Two"    //若是有分支能夠用一樣的方式處理的話,分支條件能夠連在一塊兒:
          s.contains("hello") -> "it's a welcome!
         parseInt(s) -> print("s encode x") //能夠用任意表達式做爲分支的條件
        in 1..10     -> print("x is in the range")//甚至能夠用 in 或者 !in 檢查值是否值在一個範圍或一個集合中:
        "Hello"     -> "Greeting"
          is TextView -> obj.setText("I'm a TextView")//後臺自動轉型
        is Long     -> "Long"//也能夠用 is 或者 !is 來判斷值是不是某個類型。
        !is String     -> "不是字符串"
        else         -> "未知格式"//也能夠用來代替 if-else if 。若是沒有任何參數提供,那麼分支的條件就是簡單的布爾表達式,當條件爲真時執行相應的分支:
    }
}
  • 由於它是一個表達式,它也能夠返回一個值。
/*
咱們須要考慮何時做爲一個表達式使用
--》它必需要覆蓋全部分支的可能性
--》或者實現 else  分支。
不然它不會被編譯成功
*/
val result = when (x) {
        0, 1 -> "binary"
        else -> "error"
    }
## 4.3 if 表達式

在 Kotlin 中,if 是表達式,它能夠返回一個值。所以Kotlin沒有三元運算符(condition ? then : else),由於if語句已經知足了效果。

​```kotlin
// 傳統用法
var max = a
if (a < b)
    max = b

// 帶 else
var max: Int
if (a > b)
    max = a
else
    max = b

// 做爲表達式:至關於--》三目運算法
val max = if (a > b) a else b

if分支能夠做爲塊,最後一個表達式是該塊的值:

val max = if (a > b){
    print("Choose a")
    a
}
else{
    print("Choose b")
    b
}
//若是使用If做爲表達式而不是語句(例如,返回其值或將其賦值給一個變量),則須要有一個else分支。

4.3 IO

讀取操做

//逐行讀取:
var source = File("coffeetime-kotlin/myfile.txt")
val lines = source.readLines("UTF-8")

for (l in lines) {
    println(l)
}
//讀取到字符串中
val contents = source.readText("UTF-8")

4.4 返回&跳轉

Kotlin 有三種結構化跳轉表達式:

  • return 默認從最直接包圍它的函數或者匿名函數返回。
  • break 終止最直接包圍它的循環。
  • continue 繼續下一次最直接包圍它的循環。

5. 操做符

5.1 一元運算符

表達式 轉換
+a a.plus()
-a a.minus()
!a a.not()
a++ a.inc() + see below
a-- a.dec() + see below

5.2 二元操做符

表達式 轉換
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.mod(b)
a..b a.rangeTo(b)
a in b b.contains(a)
a !in b !b.contains(a)

5.3 數組操做符、等於操做符、函數調用

in 和 !in他們的產生步驟是同樣的,但參數順序是相反的。

數組操做符 函數
a[i] a.get(i)
a[i, j] a.get(i, j)
a[i_1, ..., i_n] a.get(i_1, ... , i_n)
a[i] = b a.set(i, b)
a[i,j] =b a.set(i, j, b)
a[i_1, ... , i_n] = b a.set(i_1,... ,o_n,b)
等於操做符 函數
a == b a?.equals(b) ?: b === null
a != b !(a?.equals(b) ?: b === null)
函數方法 函數調用
a(i) a.invoke(i)
a(i, j) a.invoke(i, j)
a(i_1, ... , i_n) a.invoke(i_1, ..., i_n)

5.4 空安全

5.4.1 可空類型和非空類型

在 Kotlin 類型系統中能夠爲空和不可爲空的引用是不一樣的。好比:

  • 不能爲空:
var a: String ="abc"
a = null //編譯不經過
  • 容許爲空
var b: String? = "abc"
b = null
  • 如今你能夠調用 a 的方法,而不用擔憂 NPE 異常了:
val l = a.length()
  • 但若是你想使用 b 調用一樣的方法就有可能報錯了:
val l = b.length() //錯誤:b 不可爲空
  • 但咱們任然想要調用方法,有些辦法能夠解決。
val l = b?.length() 
val l = b!!.length()

#### 5.4.1.1 Elvis 操做符

val l = b.length()?: -1
//結合return與throw
fun foo(node: Node): String? {
  val parent = node.getParent() ?: return null
  val name = node.getName() ?: throw IllegalArgumentException("name expected")

  //...
}

5.4.2 在條件中檢查 null

首先,你能夠檢查 b 是否爲空,而且分開處理下面選項:

val l = if (b != null) b.length() else -1

編譯器會跟蹤你檢查的信息並容許在 if 中調用 length()。更復雜的條件也是能夠的:

if (b != null && b.length() >0)
  print("Stirng of length ${b.length}")
else
  print("Empty string")

注意只有在 b 是不可變時才能夠

5.4.3 安全調用

第二個選擇就是使用安全操做符,?.:

b?.length()
//若是 b 不爲空則返回長度,不然返回空。這個表達式的的類型是 Int?

安全調用在鏈式調用是是頗有用的。好比,若是 Bob 是一個僱員可能分配部門(也可能不分配),若是咱們想獲取 Bob 的部門名做爲名字的前綴,就能夠這樣作:

bob?.department?.head?.name

這樣的調用鏈在任何一個屬性爲空都會返回空。

5.5 !!操做符

第三個選擇是 NPE-lovers。咱們能夠用 b!! ,這會返回一個非空的 b 或者拋出一個 b 爲空的 NPE

val l = b !!.length()

5.6 安全轉換

普通的轉換可能產生 ClassCastException 異常。另外一個選擇就是使用安全轉換,若是不成功就返回空:

val aInt: Int? = a as? Int

5.7 Kotlin中的null安全

  • 在Kotlin中一切都是對象(甚至是Java中原始數據類型),一切都是可null的。
//一個可null的integer:
    val a: Int? = null
//一個可nul類型,你在沒有進行檢查以前你是不能直接使用它。這個代碼不能被編譯:
  val a: Int? = null
  a.toString()
//前一行代碼標記爲可null,而後編譯器就會知道它,因此在你null檢查以前你不能去使用它。
//還有一個特性是當咱們檢查了一個對象的可null性,以後這個對象就會自動轉型成不可null類型,這就是Kotlin編譯器的智能轉換:
  vala:Int?=null
  ...
  if(a!=null){
      a.toString()
  }
//在 if 中,a 從Int?變成了Int,因此咱們能夠不須要再檢查可null性而直接使用它。 
//if代碼以外,固然咱們又得檢查處理。這僅僅在變量當前不能被改變的時候纔有效,由於不然這個value可能被另外的線程修改,這時前面的檢查會返回false。 
//val 屬性或者本地( val or var  )變量。這聽起來會讓事情變得更多。難道咱們不得不去編寫大量代碼去進行可null性的檢查?固然不是,首先,由於大多數時候你不須要使用null類型。Null引用沒有咱們想象中的有用,當清除覺得null時你就會發現這一點。可是Kotlin也有它本身的使處理更簡潔的方案。

//舉個例子,咱們以下簡化代碼:
  val a: Int? = null
  ...
  a?.toString()
//這裏咱們使用了安全訪問操做符( ?  )。
//只有這個變量不是null的時候纔會去執行前面的那行代碼。不然,它不會作任何事情。而且咱們甚至可使用Elvis
  operator( ?:  ):
  val a:Int? = null
  val myString = a?.toString() ?: ""
//由於在Kotlin中 throw 和 return  都是表達式,他們能夠用在Elvis operator操做符的右邊:
  val myString = a?.toString() ?: return false
  val myString = a?.toString() ?: throw IllegalStateException()
//-----------------------------------------------------------------------------------------------------------------------
//而後,咱們可能會遇到這種情景,咱們肯定咱們是在用一個非null變量,可是他的類型倒是可null的。
//咱們可使用 !!  操做符來強制編譯器執行可null類型時跳過限制檢查:
  val a: Int? = null
  a!!.toString()
//上面的代碼將會被編譯,可是很顯然會奔潰。因此咱們要確保只能在特定的狀況下使用。一般咱們能夠本身選擇做爲解決方案。
//若是一份代碼滿篇都是 !!  ,那就有股代碼沒有被正確處理的氣味了。

6. Android使用

6.1 Get 和Set

在Kotlin中,只須要一個屬性就能夠了:

public class Person {
    var name: String = ""
}
...
val person = Person()
person.name = "name"
val name = person.name

若是沒有任何指定,屬性會默認使用getter和setter。固然它也能夠修改成你自定義的代碼,而且不修改存在的代碼:

public classs Person {
  var name: String = ""
         get() = field.toUpperCase()
      set(value){
          field = "Name: $value"
    }
}

若是須要在getter和setter中訪問這個屬性自身的值,它須要建立一個 backing field 。可使用 field 這個預留字段來訪問,它會被編譯器找到正在使用的並自動建立。須要注意的是,若是咱們直接調用了屬性,那咱們會使用setter和getter,而不是直接訪問這個屬性。 backing field 只能在屬性訪問器內訪問。

就如在前面章節提到的,當操做Java代碼的時候,Kotlin將容許使用屬性的語法去訪問在Java文件中定義的getter/setter方法。編譯器會直接連接到它原始的getter/setter方法。因此當咱們直接訪問屬性的時候不會有性能開銷。

6.2 Lambdas

  • 簡化setOnClickListener()
//首先要編寫一個 OnClickListener  接口:
public interface OnClickListener {
    void onClick(View v);
}
//編寫一個匿名內部類去實現這個接口
view.setOnClickListener(new OnClickListener(){
    @Override
    public void onClick(View v) {
        Toast.makeText(v.getContext(), "Click", Toast.LENGTH_SHORT).show();
    }
})
//把上面的代碼轉換成Kotlin(使用了Anko的toast函數):
view.setOnClickListener(object : OnClickListener {
    override fun onClick(v: View) {
        toast("Click")
    }
}
//Kotlin容許Java庫的一些優化,Interface中包含單個函數能夠被替代爲一個函數。若是咱們這麼去定義了,它會正常執行:
fun setOnClickListener(listener: (View) -> Unit)
//一個lambda表達式經過參數的形式被定義在箭頭的左邊(被圓括號包圍),而後在箭頭的右邊返回結果值。在這個例子中,咱們接收一個View,而後返回一個Unit(沒有東西)。因此根據這種思想,咱們能夠把前面的代碼簡化成這樣:
view.setOnClickListener({
      view -> toast("Click")
})
//當咱們定義了一個方法,咱們必須使用大括號包圍,而後在箭頭的左邊指定參數,在箭頭的右邊返回函數執行的結果。若是左邊的參數沒有使用到,咱們甚至能夠省略左邊的參數:
view.setOnClickListener({ 
      toast("Click") 
})
//若是這個函數的最後一個參數是一個函數,咱們能夠把這個函數移動到圓括號外面:
view.setOnClickListener() { 
  toast("Click") 
}
//而且,最後,若是這個函數只有一個參數,咱們能夠省略這個圓括號:
view.setOnClickListener { toast("Click")}
//比原始的Java代碼簡短了5倍多。

6.3 標準委託

在Kotlin的標準庫中有一系列的標準委託。它們包括了大部分有用的委託,可是咱們也能夠建立咱們本身的委託。

6.3.1 Lazy

它包含一個lambda,當第一次執行 getValue 的時候這個lambda會被調用,因此這個屬性能夠被延遲初始化。以後的調用都只會返回同一個值。這是很是有趣特性, 當咱們在它們第一次真正調用以前不是必須須要它們的時候。咱們能夠節省內存,在這些屬性真正須要前不進行初始化。

class App : Application() {
  val database: SQLiteOpenHelper by lazy {
          MyDatabaseHelper(applicationContext)
  }
  override fun onCreate() {
          super.onCreate()
          val db = database.writableDatabase
  }
}
//在這個例子中,database並無被真正初始化,直到第一次調用 onCreate 時。
//在那以後,咱們才確保applicationContext存在,而且已經準備好能夠被使用了。 
//lazy  操做符是線程安全的。若是你不擔憂多線程問題或者想提升更多的性能,你也可使用 
lazy(LazyThreadSafeMode.NONE){ 
  ...
}

6.3.2 Observable

這個委託會幫咱們監測咱們但願觀察的屬性的變化。當被觀察屬性的 set 方法被調用的時候,它就會自動執行咱們指定的lambda表達式。因此一旦該屬性被賦了新的值,咱們就會接收到被委託的屬性、舊值和新值。

//這個例子展現了,一些咱們須要關心的ViewMode,每次值被修改了,就會保存它們到數據庫。
class ViewModel(val db: MyDatabase) {
  var myProperty by Delegates.observable("") {
      d, old, new ->
      db.saveChanges(this, new)
  }
}

6.3.3 Vetoable

這是一個特殊的 observable ,它讓你決定是否這個值須要被保存。它能夠被用於在真正保存以前進行一些條件判斷。

//這個委託只容許在新的值是正數的時候執行保存。在lambda中,最後一行表示返回值。你不須要使用return關鍵字(實質上不能被編譯)。
var positiveNumber = Delegates.vetoable(0) {
      d, old, new ->
      new >= 0
}

6.3.4 Not Null

有時候咱們須要在某些地方初始化這個屬性,可是咱們不能在構造函數中肯定,或者咱們不能在構造函數中作任何事情。第二種狀況在Android中很常見:在Activity、fragment、service、receivers……不管如何,一個非抽象的屬性在構造函數執行完以前須要被賦值。爲了給這些屬性賦值,咱們沒法讓它一直等待到咱們但願給它賦值的時候。咱們至少有兩種選擇方案。第一種就是使用可null類型而且賦值爲null,直到咱們有了真正想賦的值。可是咱們就須要在每一個地方無論是不是null都要去檢查。若是咱們肯定這個屬性在任何咱們使用的時候都不會是null,這可能會使得咱們要編寫一些必要的代碼了。第二種選擇是使用 notNull 委託。它會含有一個可null的變量並會在咱們設置這個屬性的時候分配一個真實的值。若是這個值在被獲取以前沒有被分配,它就會拋出一個異常。
這個在單例App這個例子中頗有用:

class App : Application() {
      companion object {
        var instance: App by Delegates.notNull()
    }
     override fun onCreate() {
        super.onCreate()
        instance = this
    }
}

7 操做符

7.1 總數操做符

7.1.1 any

//若是至少有一個元素符合給出的判斷條件,則返回true。
val list = listOf(1, 2, 3, 4, 5, 6)
assertTrue(list.any { it % 2 == 0 })
assertFalse(list.any { it > 10 })

7.1.2 all

//若是所有的元素符合給出的判斷條件,則返回true。
assertTrue(list.all { it < 10 })
assertFalse(list.all { it % 2 == 0 })

7.1.3 count

//返回符合給出判斷條件的元素總數。
assertEquals(3, list.count { it % 2 == 0 })

7.1.4 fold

//在一個初始值的基礎上從第一項到最後一項經過一個函數累計全部的元素。
assertEquals(25, list.fold(4) { total, next -> total + next })

7.1.5 foldRight

//與 fold  同樣,可是順序是從最後一項到第一項。
assertEquals(25, list.foldRight(4) { total, next -> total + next
})

7.1.6 forEach

//遍歷全部元素,並執行給定的操做。
list.forEach { println(it) }

7.1.7 forEachIndexed

//與 forEach  ,可是咱們同時能夠獲得元素的index。
list.forEachIndexed { index, value
-> println("position index contains a value") }

7.1.8 max

//返回最大的一項,若是沒有則返回null。
assertEquals(6, list.max())

7.1.9 maxBy

//根據給定的函數返回最大的一項,若是沒有則返回null。
assertEquals(1, list.maxBy { -it })// The element whose negative is greater

7.1.10 min

//返回最小的一項,若是沒有則返回null。
assertEquals(1, list.min())

7.1.11 minBy

//根據給定的函數返回最小的一項,若是沒有則返回null。
assertEquals(6, list.minBy { -it })// The element whose negative is smaller

7.1.12 none

//若是沒有任何元素與給定的函數匹配,則返回true。
assertTrue(list.none { it % 7 == 0 })// No elements are divisible by 7

7.1.13 reduce

//與 fold  同樣,可是沒有一個初始值。經過一個函數從第一項到最後一項進行累計。
assertEquals(21, list.reduce { total, next -> total + next })

7.1.14 reduceRight

//與 reduce  同樣,可是順序是從最後一項到第一項。
assertEquals(21, list.reduceRight { total, next -> total + next
})

7.1.14 sumBy

//返回全部每一項經過函數轉換以後的數據的總和。
assertEquals(3, list.sumBy { it % 2 })

7.1.15 Example

val listIntTotal = listOf(1, 2, 3, 4, 5, 6)
val listStrTotal  = listOf("yes", "no", "sure")

fun main(args: Array<String>) {
    //1.any 若是至少有一個元素符合給出的判斷條件,則返回true
    println(listIntTotal.any { it % 2 == 0 })//true
    println("--------------any1 -------------------------------")
    println(listIntTotal.any { it > 10 })//false
    println("--------------any2 -------------------------------")
    //2.all 若是所有的元素符合給出的判斷條件,則返回true
    println(listIntTotal.all { it < 10 })//true
    println("--------------all1 -------------------------------")
    println(listIntTotal.all { it % 2 == 0 })//false
    println("--------------all2 -------------------------------")
    //3.count 返回符合給出判斷條件的元素總數
    println(listIntTotal.count { it % 2 == 0 })//3
    println("--------------count-------------------------------")
    //4.fold 在一個初始值的基礎上從第一項到最後一項經過一個函數累計全部的元素
    //其實fold 就是摺疊的意思,把一個集合的元素摺疊起來的並獲得一個最終的結果
    //從4開始,1+4,2+5,3+7,4+10,5+14,6+19=25
    println(listIntTotal.fold(4) { total, next -> total + next })//25
    println("--------------fold1-------------------------------")
    //將int 轉換爲字符串
    val r1 = listIntTotal.fold(StringBuilder()) {
        strBuilder, it ->
        strBuilder.append(it).append(",")
    }
    println(r1.toString() is String)
    println("--------------fold2-------------------------------")
    //5.foldRight 與 fold 同樣,可是順序是從最後一項到第一項
    //從4開始,4+6,10+5,15+4,19+3,22+2,24+1=25
    println(listIntTotal.foldRight(4) { total, next -> total + next })//25
    println("--------------foldRight-------------------------------")
    //6.reduce 與 fold 同樣,可是沒有一個初始值。經過一個函數從第一項到最後一項進行累計
    println(factorial(5))//120
    println("--------------factorial-------------------------------")
    println(listIntTotal.reduce { total, next -> total + next })//21
    println("--------------reduce-------------------------------")
    println(listIntTotal.reduceRight { i, acc -> i + acc })//21
    println("--------------reduceRight-------------------------------")
    //7.forEach
    listStrTotal.forEach { println(it) }
    println("--------------forEach-------------------------------")
    //與forEach相似 可是咱們同時能夠獲得元素的index
    listStrTotal.forEachIndexed { index, s -> println("$index $s") }
    println("--------------forEachIndexed-------------------------------")
    listStrTotal.forEachWithIndex { i, s -> println("$i $s") }
    println("--------------forEachWithIndex-------------------------------")
    listStrTotal.forEachReversedWithIndex { i, s -> println("$i $s") }
    println("--------------forEachReversedWithIndex-------------------------------")
    listStrTotal.forEachReversed { println(it) }
    println("--------------forEachReversed-------------------------------")
    listStrTotal.forEachByIndex { println(it) }
    println("--------------forEachByIndex-------------------------------")
    //8.max 返回最大的一項,若是沒有則返回null
    println(listIntTotal.max())
    println("--------------max-------------------------------")
    println(listIntTotal.maxBy { -(4 - it * 8) })
    println("--------------maxBy-------------------------------")
    //9.min 返回最小的一項,若是沒有則返回null
    println(listIntTotal.min())
    println("--------------min-------------------------------")
    println(listIntTotal.minBy { -(4 - it * 8) })
    println("--------------minBy-------------------------------")
    //10.none 若是沒有任何元素與給定的函數匹配,則返回true。
    println(listIntTotal.none())//false
    println("--------------none-------------------------------")
    println(listIntTotal.none { it % 3 == 0 })//false
    println("--------------none-------------------------------")
    //11.sumBy 返回全部每一項經過函數轉換以後的數據的總和
    println(listIntTotal.sumBy { it % 2 })//3
    println("--------------sumBy-------------------------------")
}

7.2 過濾操做符

7.2.1 drop

//返回包含去掉前n個元素的全部元素的列表。
assertEquals(listOf(5, 6), list.drop(4))

7.2.2 dropWhile

//返回根據給定函數從第一項開始去掉指定元素的列表。
assertEquals(listOf(3, 4, 5, 6), list.dropWhile { it < 3 })

7.2.3 dropLastWhile

//返回根據給定函數從最後一項開始去掉指定元素的列表。
assertEquals(listOf(1, 2, 3, 4), list.dropLastWhile { it > 4 })

7.2.4 filter

//過濾全部符合給定函數條件的元素。
assertEquals(listOf(2, 4, 6), list .ilter { it % 2 == 0 })

7.2.5 filterNot

//過濾全部不符合給定函數條件的元素。
assertEquals(listOf(1, 3, 5), list.filterNot { it % 2 == 0 })

7.2.6 filterNotNull

//過濾全部元素中不是null的元素。
assertEquals(listOf(1, 2, 3, 4), listWithNull.filterNotNull())

7.2.7 slice

//過濾一個list中指定index的元素。
assertEquals(listOf(2, 4, 5), list.slice(listOf(1, 3, 4)))

7.2.8 take

//返回從第一個開始的n個元素。
assertEquals(listOf(1, 2), list.take(2))

7.2.9 takeLast

//返回從最後一個開始的n個元素
assertEquals(listOf(5, 6), list.takeLast(2))

7.2.10 takeWhile

//返回從第一個開始符合給定函數條件的元素。//
assertEquals(listOf(1, 2), list.takeWhile { it < 3 })

7.2.11 Example

val listIntFilter = listOf(1, 2, 3, 4, 5, 6)

fun main(args: Array<String>) {
    //1.drop 返回包含去掉前n個元素的全部元素的列表
    (listIntFilter.drop(3)).forEach { println(it) }//4,5,6
    println("--------------drop -------------------------------")
    //2.filter 過濾全部符合給定函數條件的元素
    (listIntFilter.filter { it % 2 == 0 }).forEach { println(it) }//2,4,6
    println("--------------filter -------------------------------")
    //3.filterNot 過濾全部不符合給定函數條件的元素
    (listIntFilter.filterNot { it % 2 == 0 }).forEach { println(it) }//1,3,5
    println("--------------filterNot -------------------------------")
    //4.slice 過濾掉非指定下標的元素,即保留下標對應的元素過濾List中指定下標的元素(好比這裏只保留下標爲1,3,4的元素),當過濾list中有元素值大於目標List大小時會出現異常
    (listIntFilter.slice(listOf(1, 4)).forEach { println(it) })//2,5
    println("--------------slice -------------------------------")
    //5.take 返回從第一個開始的n個元素
    (listIntFilter.take(3)).forEach { println(it) }//1,2,3
    println("--------------take -------------------------------")
    //6.takeLast 返回從最後一個開始的n個元素
    (listIntFilter.takeLast(3)).forEach { println(it) }//4,5,6
    println("--------------takeLast -------------------------------")
    //7.takeWhile 返回從第一個開始符合給定函數條件的元素
    (listIntFilter.takeWhile { it < 3 }).forEach { println(it) }//1,2
    println("--------------takeWhile -------------------------------")
}

7.3 映射操做符

7.3.1 flatMap

//遍歷全部的元素,爲每個建立一個集合,最後把全部的集合放在一個集合中。
assertEquals(listOf(1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7),
list.flatMap { listOf(it, it + 1) })

7.3.2 groupBy

//返回一個根據給定函數分組後的map。
assertEquals(mapOf("odd" to listOf(1, 3, 5), "even" to listOf(2,
4, 6)), list.groupBy { if (it % 2 == 0) "even" else "odd" })

7.3.3 map

//返回一個每個元素根據給定的函數轉換所組成的List。
assertEquals(listOf(2, 4, 6, 8, 10, 12), list.map { it * 2 })

7.3.4 mapIndexed

//返回一個每個元素根據給定的包含元素index的函數轉換所組成的List。
assertEquals(listOf (0, 2, 6, 12, 20, 30), list.mapIndexed { ind
ex, it -> index * it })

7.3.5 mapNotNull

//返回一個每個非null元素根據給定的函數轉換所組成的List。
assertEquals(listOf(2, 4, 6, 8), listWithNull.mapNotNull { it * 2
})

7.3.6 Example

val listIntMap = listOf(1, 2, 3, 4, 5, 6)
val listStrMap = listOf("y,s", "n,o", "su,e")

fun main(args: Array<String>) {
    //1.flatMap 遍歷全部的元素,爲每個建立一個集合,最後把全部的集合放在一個集合中
    (listStrMap.flatMap { it.split(",") }).forEach { print(it) }
    (listStrMap.flatMap { it.split(",") }).map { println(it) }

    val list = listOf(
            1..20,
            2..5
    )
    (list.flatMap { it }).forEach { println(it) }
    (list.flatMap { it.map { it * 2 } }).forEach { println(it) }

    println("--------------flatMap -------------------------------")
    //2.groupBy 返回一個根據給定函數分組後的map
    (listIntMap.groupBy { if (it > 4) "大於4" else "小於4" }).forEach { t, u -> println("$t $u") }
    println("--------------groupBy -------------------------------")
    //3.map 返回一個每個元素根據給定的函數轉換所組成的List
    (listIntMap.map { it * 2 }).forEach { println(it) }
    println("--------------map -------------------------------")
    //4.mapIndexed 返回一個每個元素根據給定的包含元素index的函數轉換所組成的List
    listIntMap.mapIndexed { index, i -> println("$index $i") }
    println("--------------mapIndexed -------------------------------")
}

7.4 元素操做符

7.4.1 contains

//若是指定元素能夠在集合中找到,則返回true。
assertTrue(list.contains(2))

7.4.2 elementAt

//返回給定index對應的元素,若是index數組越界則會拋出 IndexOutOfBoundsException  。
assertEquals(2, list.elementAt(1))

7.4.3 elementAtOrElse

//返回給定index對應的元素,若是index數組越界則會根據給定函數返回默認值。
assertEquals(20, list.elementAtOrElse(10, { 2 * it }))

7.4.4 elementAtOrNull

//返回給定index對應的元素,若是index數組越界則會返回null。
assertNull(list.elementAtOrNull(10))

7.4.5 first

//返回符合給定函數條件的第一個元素。
assertEquals(2, list.first { it % 2 == 0 })

7.4.6 firstOrNull

//返回符合給定函數條件的第一個元素,若是沒有符合則返回null。
assertNull(list.firstOrNull { it % 7 == 0 })

7.4.7 indexOf

//返回指定元素的第一個index,若是不存在,則返回 -1  。
assertEquals(3, list.indexOf(4))

7.4.8 indexOfFirst

//返回第一個符合給定函數條件的元素的index,若是沒有符合則返回 -1  。
assertEquals(1, list.indexOfFirst { it % 2 == 0 })

7.4.9 indexOfLast

//返回最後一個符合給定函數條件的元素的index,若是沒有符合則返回 -1  。
assertEquals(5, list.indexOfLast { it % 2 == 0 })

7.4.10 last

//返回符合給定函數條件的最後一個元素。
assertEquals(6, list.last { it % 2 == 0 })

7.4.11 lastIndexOf

返回指定元素的最後一個index,若是不存在,則返回 -1 。

7.4.12 lastOrNull

//返回符合給定函數條件的最後一個元素,若是沒有符合則返回null。
val list = listOf(1, 2, 3, 4, 5, 6)
assertNull(list.lastOrNull { it % 7 == 0 })

7.4.13 single

//返回符合給定函數的單個元素,若是沒有符合或者超過一個,則拋出異常。
assertEquals(5, list.single { it % 5 == 0 })

7.4.14 singleOrNull

//返回符合給定函數的單個元素,若是沒有符合或者超過一個,則返回null。
assertNull(list.singleOrNull { it % 7 == 0 })

7.4.15 Example

val listIntElement = listOf(1, 2, 3, 4, 5, 6)

fun main(args: Array<String>) {
    //1.contains 若是指定元素能夠在集合中找到,則返回true
    println(listIntElement.contains(3))//true
    println("--------------contains -------------------------------")
    //2.elementAt 返回給定index對應的元素,若是index數組越界則會拋出 IndexOutOfBoundsException
    println(listIntElement.elementAt(2))//3
    println("--------------elementAt -------------------------------")
    //3.elementAtOrElse 返回給定index對應的元素,若是index數組越界則會根據給定函數返回默認值
    println(listIntElement.elementAtOrElse(3, { "沒有哦" }))//4
    println(listIntElement.elementAtOrElse(8, { "沒有哦" }))//沒有哦
    println("--------------elementAtOrElse -------------------------------")
    //4.first 返回符合給定函數條件的第一個元素
    println(listIntElement.first())//1
    println(listIntElement.first{ it % 3 == 0 })//3
    println("--------------first -------------------------------")
    //5.indexOf 返回指定元素的第一個index,若是不存在,則返回 -1
    println(listIntElement.indexOf(3))//2
    println(listIntElement.indexOf(8))//-1
    println("--------------indexOf -------------------------------")
    //6.indexOfFirst 返回第一個符合給定函數條件的元素的index,若是沒有符合則返回 -1
    println(listIntElement.indexOfFirst{ it % 3 == 0 })//2
    println("--------------indexOfFirst -------------------------------")
    //同理還有indexOfLast,表示返回最後一個符合給定函數條件的元素的index,若是沒有符合則返回 -1
    println(listIntElement.indexOfLast{ it % 3 == 0 })//5
    println("--------------indexOfLast -------------------------------")
    //7.last 返回符合給定函數條件的最後一個元素
    println(listIntElement.last())//6
    println("--------------last -------------------------------")
    //8.lastIndexOf 返回指定元素的最後一個index,若是不存在,則返回 -1
    println(listIntElement.lastIndexOf(2))//1 若是list爲(1, 2, 3, 4, 2, 5, 6)則結果爲4
    println("--------------lastIndexOf -------------------------------")
}

7.5 生產操做符

7.5.1 merge

//把兩個集合合併成一個新的,相同index的元素經過給定的函數進行合併成新的元素做爲新的集合的一個元素,返回這個新的集合。
//新的集合的大小由最小的那個集合大小決定。
val list = listOf(1, 2, 3, 4, 5, 6)
val listRepeated = listOf(2, 2, 3, 4, 5, 5, 6)
assertEquals(listOf(3, 4, 6, 8, 10, 11), list.merge(listRepeated
) { it1, it2 -> it1 + it2 })

7.5.2 partition

//把一個給定的集合分割成兩個,第一個集合是由原集合每一項元素匹配給定函數條件返回 true  的元素組成,
//第二個集合是由原集合每一項元素匹配給定函數條件返回 false  的元素組成。
assertEquals(
    Pair(listOf(2, 4, 6), listOf(1, 3, 5)) , list.partition { it % 2 == 0 }    
)

7.5.3 plus

//返回一個包含原集合和給定集合中全部元素的集合,由於函數的名字緣由,咱們可使用 +  操做符。
assertEquals(
    listOf(1, 2, 3, 4, 5, 6, 7, 8) , list + listOf(7, 8)
)

7.5.4 zip

//返回由 pair  組成的List,每一個 pair  由兩個集合中相同index的元素組成。這個返
回的List的大小由最小的那個集合決定。
assertEquals(
    listOf(Pair(1, 7), Pair(2, 8)) , list.zip(listOf(7, 8))
)

7.5.5 unzip

//從包含pair的List中生成包含List的Pair。
assertEquals(
    Pair(listOf(5, 6), listOf(7, 8)) , listOf(Pair(5, 7), Pair(6, 8)).unzip()
)

7.5.6 Example

val listIntProduct1 = listOf(1, 2, 3, 4, 5, 6)
val listIntProduct2 = listOf(3, 5, 7, 9)

fun main(args: Array<String>) {
    //1.partition 把一個給定的集合分割成兩個,第一個集合是由原集合每一項元素匹配給定函數條件返回 true 的元素組成,第二個集合是由原集合每一項元素匹配給定函數條件返回 false 的元素組成
    println(listIntProduct1.partition { it > 3 })//([4, 5, 6], [1, 2, 3])
    println("--------------partition -------------------------------")
    //2.plus 合併兩個List,能夠用」+」替代
    println(listIntProduct1.plus(listIntProduct2))//[1, 2, 3, 4, 5, 6, 3, 5, 7, 9]
    println("--------------plus -------------------------------")
    //3.zip 兩個集合按照下標組合成一個個的Pair塞到集合中返回
    println(listIntProduct1.zip(listIntProduct2))//[1, 2, 3, 4, 5, 6, 3, 5, 7, 9]
    println("--------------zip -------------------------------")
    //merge的替代方法,把兩個集合,按照相同下標,合成新的元素,合成的集合大小由最小的集合決定
    println(listIntProduct1.zip(listIntProduct2) { it1, it2 -> it1 + it2 })//[4, 7, 10, 13]
    println("--------------zip 替代 merge-------------------------------")
}

7.6 順序操做符

7.6.1 reverse

//返回一個與指定list相反順序的list。
val unsortedList = listOf(3, 2, 7, 5)
assertEquals(listOf(5, 7, 2, 3), unsortedList.reverse())

7.6.2 sort

//返回一個天然排序後的list。
assertEquals(listOf(2, 3, 5, 7), unsortedList.sort())

7.6.3 sortBy

//返回一個根據指定函數排序後的list。
assertEquals(listOf(3, 7, 2, 5), unsortedList.sortBy { it % 3 })

7.6.4 sortDescending

//返回一個降序排序後的List。
assertEquals(listOf(7, 5, 3, 2), unsortedList.sortDescending())

7.6.5 sortDescendingBy

//返回一個根據指定函數降序排序後的list。
assertEquals(
  listOf(2, 5, 7, 3),
  unsortedList.sortDescendingBy {
    it % 3 
})

7.6.6 Example

val listIntOrder1 = listOf(1, 2, 3, 4, 5, 6)
val listIntOrder2 = listOf(5, 2, 4, 8, 1, 6)

fun main(args: Array<String>) {
    //1.reverse  返回一個與指定list相反順序的list
    println(listIntOrder1.reversed())//[6, 5, 4, 3, 2, 1]
    println("--------------reversed-------------------------------")
    //2.sorted 返回一個天然排序後的list
    println(listIntOrder2.sorted())//[1, 2, 4, 5, 6, 8]
    println("--------------sorted-------------------------------")
    //3.sortedBy 返回一個根據指定函數排序後的list
    println(listIntOrder1.sortedBy { it * 2 })//[1, 2, 4, 5, 6, 8]
    println("--------------sortedBy-------------------------------")
    //4.sortDescending  返回一個降序排序後的List
    println(listIntOrder1.sortedDescending())//[6, 5, 4, 3, 2, 1]
    println("--------------sortedDescending-------------------------------")
    //5.sortDescendingBy  返回一個根據指定函數降序排序後的list
    println(listIntOrder1.sortedByDescending { it % 2 })//[1, 3, 5, 2, 4, 6]
    println("--------------sortedByDescending-------------------------------")
}


8 其餘

8.1 內部類&嵌套類

嵌套類

//在Java中,能夠在類的裏面再定義類。若是它是一個一般的類,它不能去訪問外部類的成員(就如Java中的static):
class Outer {
  private val bar: Int = 1
  class Nested {
          fun foo() = bar//編譯不經過
  }
}

內部類

//若是須要去訪問外部類的成員,咱們須要用 inner 聲明這個類:
class Outer {
  private val bar: Int = 1
  inner class Inner{
          fun foo() = bar//沒問題
  }
}
val demo = Outer().Inner().foo() // == 1

8.2 枚舉類

1.Kotlin提供了枚舉(enums)的實現:
  enum class Day {
      SUNDAY, MONDAY, TUESDAY, WEDNESDAY,HURSDAY, FRIDAY, SATURDAY
  }

2.枚舉能夠帶有參數:
  enum class Icon(val id: Int,var desc:String) {
      UO(1,"aa"), Search(2,"bb"), Cast(3,"cc")
  }
  val searchIconRes = Icon.Search.id//結果:2
  var a = Icon.Search.desc//結果:"bb"

3.枚舉能夠經過 String  匹配名字來獲取,咱們也能夠獲取包含全部枚舉的 Array  ,因此咱們能夠遍歷它。
  val search: Icon = Icon.valueOf("Search")
  val iconList: Array<Icon> = Icon.values()
  iconList.forEach {print("$it")}//結果:UOSearchCast
4.每個枚舉都有一些函數來獲取它的名字、聲明的位置:
  val searchName: String = Icon.SEARCH.name()
  val searchPosition: Int = Icon.SEARCH.ordinal()
  println(Icon.Search.name+"---"+Icon.Search.ordinal)//結果:Search---1
//枚舉根據它的順序實現了Comparable接口,因此能夠很方便地把它們進行排序。

8.3 密封類(Sealed)

  • 密封類用來限制類的繼承關係,這意味着密封類的子類數量是固定的。
  • 看起來就像是枚舉那樣,當你想在一個密封類的子類中尋找一個指定的類的時候,你能夠事先知道全部的子類。
  • 不一樣之處在於枚舉的實例是惟一的,而密封類能夠有不少實例,它們能夠有不一樣的狀態。
  • 咱們能夠實現,好比相似Scala中的 Option 類:這種類型能夠防止null的使用,當對象包含一個值時返回 Some 類,當對象爲空時則返回 None :
sealed class Option<out T> {
    class Some<out T> : Option<T>()
    object None : Option<Nothing>()
  }
//有一件關於密封類很不錯的事情是當咱們使用 when  表達式時,咱們能夠匹配全部選項而不使用 else分支:
  val result = when (option) {
    is Option.Some<*> -> "Contains a value"
    is Option.None -> "Empty"
  }

8.4 異常(Exceptions)

  • 在Kotlin中,全部的 Exception 都是實現了 Throwable ,含有一個 message 且未經檢查。
  • 這表示咱們不會強迫咱們在任何地方使用 try/catch 。
  • 這與Java中不太同樣,好比在拋出 IOException 的方法,咱們須要使用 try-catch 包圍代碼塊。
  • 經過檢查exception來處理顯示並非一個好的方法。
  • 像Bruce Eckel、RodWaldhoff或Anders Hejlsberg等人能夠給你關於這個更好的觀點。
1.拋出異常的方式與Java很相似:
    throw MyException("Exception message")
2.try  表達式也是相同的:
    try{
        // 一些代碼
    }catch (e: SomeException) {
        // 處理
    }finally {
      // 可選的finally塊
    }
3.在Kotlin中, throw  和 try  都是表達式,這意味着它們能夠被賦值給一個變量。
    //這個在處理一些邊界問題的時候確實很是有用:
    val s = when(x){
        is Int -> "Int instance"
        is String -> "String instance"
        else -> throw UnsupportedOperationException("Not valid type"
        )
    }

3.1.或者
    val s = try {
      x as String 
    } catch(e: ClassCastException) { 
      null
    }

8.5 泛型

1.寫法  
class Box<T>(t: T){
      var value = t
  }
2.一般來講,建立一個這樣類的實例,須要提供類型參數:
    val box: Box<Int> = Box<Int>(1)
3.可是若是泛型能夠推斷(好比:構造函數推斷),則能夠省略
    val box = Box(1)//1是 Int 型,所以編譯器會推導出咱們調用的是 Box<Int>

8.5.1 變形

  • java
interface Collection<E> ... {
          void addAll(Colletion<? extend E> items);
  }
  void copyAll(Collection<Object> to, Collection<String> from) {
        to.addAll(from); // 警告:!!! Would not compile with the naive declaration of addAll:
        //Collection<String> 繼承自 Collection<Object>
  }

2.假若有個範型接口Source<T>,沒有任何接收 T 做爲參數的方法,惟一的方法就是返回 T:
  interface Source<T> {
    T nextT();
  }
3.存儲一個Source<String>的實例引用給一個類型爲 Source<Object> 是安全的,但java並不知道
  void demo(Source<String> strs) {
    Source<Object> objects = strs; // !!! 編譯不經過
  }
  • kotlin
  • 聲明處變型

    1.經過註解類型參數 T 的來源,來確保它僅從 Source<T> 成員中返回(生產),並從不被消費。 kotlin提供 out 修飾符:

    2.通常原則是:當一個類 C 的類型參數 T 被聲明爲 out 時,它就只能出如今 C 的成員的輸出位置,結果是 C<Base> 能夠安全地做爲 C<Derived>的超類。

    3.out 修飾符被稱之爲變型註解,但因爲同處與類型參數聲明處,咱們稱之爲聲明處變型這與 Java 中的使用處變型相反。

//聲明處變型:out
  abstract class Source<out T> {
      abstract fun nextT(): T
  }
  fun demo(strs: Source<String>) {
      val objects: Source<Any> = strs
  }

4.in----out以外Kotlin 又補充了一個變型註釋,它接受一個類型參數逆變

//聲明處變型:in
  abstract class Comparable<in T> {

      abstract fun compareTo(other: T): Int
  }
  fun demo(x: Comparable<Number>) {
      x.compareTo(1.0) // 1.0 類型:Double, 屬於Number的子類
      val y: Comparable<Double> = x 
  }
//消費者 in, 生產者 out! (in 和 out 的使用參照C#
  • 使用處變型
//使用處變型:類型投影
1.聲明類型參數 T 爲 out 很方便,並且能夠避免在使用出子類型的麻煩,但有些類 不能 限制它只返回 T
    class Array<T>(val size: Int) {
        fun get(index: Int): T { /* ... */ }
        fun set(index: Int, value: T) { /* ... */ }
    }
2.聲明類型參數 T 爲 out 很方便,並且能夠避免在使用出子類型的麻煩,但有些類 不能 限制它只返回 T
    //該函數做用是複製 array 
    fun copy(from: Array<Any>, to: Array<Any>) {
        assert(from.size == to.size)
        for (i in from.indices)
            to[i] = from[i]
    }
3.遇到了一樣的問題 Array<T> 中的T 是不可變型的,所以 Array<Int> 和 Array<Any> 互不爲對方的子類,致使複製失敗。
    val ints: Array<Int> = arrayOf(1, 2, 3)
    val any = Array<Any>(3) { "" } 
    copy(ints, any) // 拋異常:ClassCastException (Array<Any>, Array<Any>)
4.類型投影
    fun copy(from: Array<out Any>, to: Array<Any>) {
     // 這裏的from不是一個簡單的 array, 而是一個投影
     //只能調用那些返回類型參數 T 的方法,在這裏意味着只能調用get()。相似 Java 中Array<? extends Object>
    }
5.用in作投影的寫法
    fun fill(dest: Array<in String>, value: String) {
        // Array<in String> 對應 Java 中的 Array<? super String>
       //fill()函數能夠接受任何CharSequence 類型或 Object類型的 array 。
    }

8.5.2 泛型函數

函數也能夠像類同樣有類型參數。類型參數在函數名以前:
fun <T> singletonList(item: T): List<T> {
    // ...
}

fun <T> T.basicToString() : String { 
  // extension function
}

8.5.3 上界(upper bound)

最經常使用的類型約束是上界,在 Java 中對應 extends關鍵字:

fun <T : Comparable<T>> sort(list: List<T>) {
    // 冒號後面指定的類型就是上界:只有 Comparable<T>的子類型才能夠取代 T 好比:
}

8.6 代理

代理模式

1.代理模式 給實現繼承提供了很好的代替方式, 因此並不須要什麼樣板代碼
2.Derived 類能夠繼承 Base 接口而且指定一個對象代理它所有的公共方法:
  interface Base {
      fun print()
  }

  class BaseImpl(val x: Int) : Base {
      override fun print() { printz(x) }
  }

  class Derived(b: Base) : Base by b //關鍵字:by

  fun main() {
      val b = BaseImpl(10)
      Derived(b).print()
  }
3.在 Derived 的父類列表中的 by 從句會將 b 存儲在 Derived 內部對象,而且編譯器會生成 Base 的全部方法並轉給 b。

代理屬性


8.7 註解

1.註解聲明:聲明註解須要在類前面使用 annotation 關鍵字
    annotation class fancy
2.用法
  @fancy class Foo {
      @fancy fun baz(@fancy foo: Int): Int {
          return (@fancy 1)
      }
  }
2.1.在多數情形中 @ 標識是可選的。只有在註解表達式或本地聲明中才必須:
  fancy class Foo {
      fancy fun baz(fancy foo: Int): Int {
          @fancy fun bar() { ... }
          return (@fancy 1)
      }
  }
2.2.若是要給構造函數註解,就須要在構造函數聲明時添加 constructor 關鍵字,而且須要在前面添加註解:+
    class Foo @inject constructor (dependency: MyDependency)
2.3.也能夠註解屬性訪問者:
    class Foo {
    var x: MyDependency?=null
        @inject set
    }
3.構造函數
    //註解能夠有帶參數的構造函數。
    annotation class special(val why: String)
    special("example") class Foo {}
4.Lambdas
  //註解也能夠用在 Lambda 中。這將會應用到 lambda 生成的 invoke() 方法。這對 Quasar框架頗有用,在這個框架中註解被用來併發控制
  annotation class Suspendable
  val f = @Suspendable { Fiber.sleep(10) }
5.java 註解在 kotlin 中是徹底兼容的:

8.7 反射

1.最基本的反射特性就是獲得運行時的類引用。要獲取引用並使之成爲靜態類可使用字面類語法:
    val c = MyClass::class
2.函數引用
    //當有一個像下面這樣的函數聲明時:
    fun isOdd(x: Int) =x % 2 !=0
    //能夠經過 isOdd(5) 輕鬆調用,一樣咱們也能夠把它做爲一個值傳遞給其它函數,操做符::
    val numbers = listOf(1, 2, 3)
    println(numbers.filter( ::isOdd) ) //prints [1, 3]
3.這裏 ::isOdd 是是一個函數類型的值 (Int) -> Boolean

4.屬性引用:訪問頂級類的屬性,也可使用 :: 操做符
    var x = 1
    fun main(args: Array<String>) {
        println(::x.get())
        ::x.set(2)//::x 表達式評估爲 KProperty<Int> 類型的屬性
        println(x)
    }
5.訪問一個類的屬性成員
      class A(val p: Int)
      fun main(args: Array<String>) {
          val prop = A::p
          println(prop.get(A(1))) // prints "1"
      }
6.與 java 反射調用
      import kotlin.reflect.jvm.*
    
      class A(val p: Int)
      fun main(args: Array<String>) {
          println(A::p.javaGetter) // 打印 "public final int A.getP()"
          println(A::p.javaField)  // 打印 "private final int A.p"
      }
7.構造函數引用
      //構造函數能夠像方法或屬性那樣引用。只須要使用 :: 操做符並加上類名。
    //下面的函數是一個沒有參數而且返回類型是 Foo:
      calss Foo
      fun function(factory : () -> Foo) {
          val x: Foo = factory()
      }
      //還能夠像下面這樣使用:
      function(:: Foo)

其餘:略...


8.8 動態類型dynamic

1.dynamic 類型關閉了 kotlin 的類型檢查:
    val dyn: dynamic = ...
2.dynamic 最奇特的特性就是能夠在 dynamic 變量上調用任何屬性或任何方法
    dyn.whatever(1, "foo", dyn) 
    dyn.whatever(*array(1, 2, 3))
3.待續...


9 Demo

1.遞歸

fun factorial(num: Int): BigInteger {
if (num == 0) return BigInteger.valueOf(1L)
return BigInteger.valueOf(num.toLong()).times(factorial(num - 1))
}

fun main(args: Array<String>) {
println(factorial(50000))}

2.尾遞歸

//尾遞歸:其實是把遞歸編譯成了迭代
class Result(var value: BigInteger = BigInteger.valueOf(1))

tailrec fun factorialWei(num: Int, result: Result) {
    if (num == 0) {
        result.value = result.value.times(BigInteger.valueOf(1L))
    } else {
        result.value = result.value.times(BigInteger.valueOf(num.toLong()))
        factorialWei(num - 1, result)
    }
}

fun main(args: Array<String>) {
    val result = Result()
    factorialWei(50000,result)
    println(result.value)
}

3.單例

1.線程安全,檢查2次

1.Java寫法
class LazyDoubleCheckJava {
    /**
     * java-5以後新增的關鍵字volatile
     * 可以保證方法的調用有序進行
     * */
    private static volatile LazyDoubleCheckJava instance;
    private LazyDoubleCheckJava() {
    }
    public static LazyDoubleCheckJava getInstance() {
        if (instance == null) {
            synchronized (LazyDoubleCheckJava.class) {
                if (instance == null) {
                    //初始化時分爲實例化,賦值2步,儘管咱們把這一步寫成下面的語句
                    //但java虛擬機並不保證其餘線程【眼中】這兩步的順序到底是怎麼樣的
                    //java5以後新增的關鍵字volatile
                    instance = new LazyDoubleCheckJava();
                }}}
        return instance;
    }}
2.kotlin寫法
class LazyDoubleCheckKotlin private constructor() {
    companion object {
        val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            LazyDoubleCheckKotlin()
        }
      -------------------------------------------------------------------------
        //另外一種等價的寫法
        private @Volatile var instance2: LazyDoubleCheckKotlin? = null
        fun getInstacne(): LazyDoubleCheckKotlin {
            if (instance2 == null) {
                synchronized(this) {
                    if (instance2 == null) {
                        instance2 = LazyDoubleCheckKotlin()
                    }}}
            return instance2!!
        }}}

2.靜態內部類

1.Java寫法
class LazyStaticInnerJava {
    private LazyStaticInnerJava() {
    }
    private static class Holder {
        private static LazyStaticInnerJava instance = new LazyStaticInnerJava();
    }
    public static LazyStaticInnerJava getInstance() {
        return Holder.instance;
    }
}
2.kotlin寫法
class LazyStaticInnerKotlin {
    companion object {
        fun getInstance() = Hoder.instance
    }
    private object Hoder {
        val instance = LazyStaticInnerKotlin()
    }
}

3.最常規寫法

1.Java寫法
class PlainOldSingletonJava {
    private static PlainOldSingletonJava java;
    private PlainOldSingletonJava() {
    }
    public static PlainOldSingletonJava getInstance() {
        return java;
    }
}
2.kotlin寫法
  object PlainOldSingletonKotlin {
}

4.懶加載-非線程安全

1.Java寫法
class LazyNoThreadSafeJava {
    private static LazyNoThreadSafeJava java;
    private LazyNoThreadSafeJava() {
    }
    public static LazyNoThreadSafeJava getInstance() {
        if (java == null) {
            java = new LazyNoThreadSafeJava();
        }
        return java;
    }
}
2.kotlin寫法
class LazyNoThreadSafeKotlin {
    //使用kotlin封裝方法
    companion object {
        val instance by lazy(LazyThreadSafetyMode.NONE) {
            LazyNoThreadSafeKotlin()
        }
    }
    //下面是另外一種等價的寫法,獲取單例使用get方法
    private var instance2: LazyNoThreadSafeKotlin? = null
    fun getInstance(): LazyNoThreadSafeKotlin {
        if (instance2 == null) {
            instance2 = singleton.LazyNoThreadSafeKotlin()
        }
        return instance2!!
    }
}

5.懶加載-線程安全

1.Java寫法
class LazyThreadSafeJava {
    private static LazyThreadSafeJava java;
    private LazyThreadSafeJava() {
    }
    public static synchronized LazyThreadSafeJava getInstance() {
        if (java == null) {
            java = new LazyThreadSafeJava();
        }
        return java;
    }
}
2.kotlin寫法
class LazyThreadSafeKotlin {
    //下面是另外一種等價的寫法,獲取單例使用get方法
    private var instance2: LazyThreadSafeKotlin? = null

    @Synchronized
    fun getInstance(): LazyThreadSafeKotlin {
        if (instance2 == null) {
            instance2 = singleton.LazyThreadSafeKotlin()
        }
        return instance2!!
    }
}
相關文章
相關標籤/搜索