Kotlin學習總結文檔

第一章 數據類型

1.基礎數據類型

Kotlin的基本數值類型包括Byte、Short、Int、Long、Float、Double等。不同於Java的是,字符不屬於數值類型。

Alt text

每個數據類型都有如下轉換函數

toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char

字面值常量
主要是以下幾種字面值常量:

--十進制數值: 123
--長整型要加大寫 L : 123L
--16進制:0x0f
--二進制:0b00001011

注意不支持8進制
Kotlin 也支持傳統的浮點數表示:

-- 默認雙精度浮點數(Double) : 123.5 , 123.5e10
-- 單精度浮點數(Float)要添加 f 或 F :123.5f

數值常量中可以添加下劃線分割(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

值相等與物理值相比較

val a: Int = 10000
print (a === a ) // 打印 'true' ---注意區別是在這個三元等號
val boxedA: Int? =a
val anotherBoxedA: Int? = a
print (boxedA === anotherBoxedA ) // 注意這裏打印的是 'false'

另一方面,它們是值相等的:

val a: Int = 10000
print(a == a) // 打印 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA) // 打印 'true'

2.變量和常量

1).變量


   //定義一個字符變量,自動推導爲字符串
   var name ="張三丰"   //也可以這樣寫  var name:String = "張三丰"
   //或者
   var addr:String="深圳"
   //或者
   var addr:String?="深圳"

2).常量

  //定義一個常量
   val company ="小牛在線"  
   //定義一個可以爲空的量
   val friend:String="張三"

3.字符串

1).字會串拼接

  var name = "XiaoMin"
  var age = 23
  var xiaomin = "小明的名字是$name,年紀是$age"
  println(xiaomin)

2).複雜字符拼接

//定義一個類
class Animal{
    var name:String="Animal"
    var age:Int = 10
}
//主函數
fun main(args: Array<String>) {
    var animal = Animal()
    println("animal name is ${animal.name}") //拼接字符
}

3).多行字符串

val text = """
    Tell me and I forget
    Teach me and I remember
    Involve me and I learn
    (Benjamin Franklin)
"""

//如果要去掉空字符,可以這樣. 類似java的trim()
var text ="""  Tell me and I forget """.trim-margin()

4.Any數據類型

Any是Kotlin所有數據的基類,類似於Java的Object. 但是它沒有hashCode,toString等方法, 這是和java的區別.


var pig:Any = Animal("pig")
var dog:Any = Animal("dog")

class Animal(name:String) {}

5.集合數據類型

1). List集合的用法

     var list = listOf<Pig>(
            Pig("大豬", 10, "F"),
            Pig("黑豬", 20, "M"),
            Pig("白豬", 30, "F"),
            Pig("小豬", 40, "M")
    )

    var list2 = listOf<Int>(
            100, 1, 2, 3, 4, 5
    )

    //直接設用java的接口對集合排序
    Collections.sort(list2)

    //調用Kotlin接口排序
    list.sortedBy({
        it.age
    })

    //輸出集合中的數據
    for ((i, item) in list2.withIndex()) {
        println("$i is $item")
    }

    for ((i, item) in list.withIndex()) {
        println("${item.name} is ${item.age}")
    }

2). Set集合的用法

    //顯示定義一個無序集合
    var addr:Set<String> = setOf("深圳","上海")
    //輸出集合數據內容
    for(item in addr.iterator()){
        println(item)
    }

    //定義有序的Set
    var friends:SortedSet<Int> = sortedSetOf(10,5,30,20,40)
    //輸出集合數據內容
    for(item in friends.iterator()){
        println(item)
    }

3).Map集合的用法

    //顯示定義一個集合
    var addr:Map<Int,String> = mapOf<Int,String>(1 to "深圳", 2 to "上海")
    //或者
    var friends = mapOf<Int,String>(1 to "小平",2 to "毛澤東")

    //輸出集合內容
    for( (K,V) in addr.entries){
        println("集合數據Key=$K,Value=$V")
    }

    for( K in addr.keys ){
        println("集合數據Key=$K,Value=${addr.get(K)}")
    }

    //定義有序的map
    var sortMap:SortedMap<Int,String> = sortedMapOf(2 to "深圳", 3 to "上海",1 to "南京")
    for( K in sortMap.keys ){
        println("有序Map Key=$K,Value=${addr.get(K)}")
    }

6.數組

數組的用法簡單好用,如下例子

fun main(args:Array<String>){

    var intArray = intArrayOf(1,2,3,4,5)

    var longArray = longArrayOf(1L,2L,3L,4L,5L)

    var floatArray = floatArrayOf(10f,20f,30f,40f,50f)

    var doubleArray = doubleArrayOf(10.1,10.2,20.3,30.3,40.4e10)

    var byteArray = byteArrayOf(1,2,3,4)

    var shortArray = shortArrayOf(1,23,3,4)

    var animals:Array<Animal> = arrayOf(Animal("狗"), Animal("豬"))
}

class Animal(name:String){
    var name:String
    init {
        this.name = name
    }
}

第二章 操作符

1.判空操作符

1.判空操作符 ?

   var name:String?="小平同志" //表示該變量可以爲空

2.如果爲空 ?:

   var name:String?="小平同志" //表示該變量可以爲空
   var rename = name?:"習近平同志" //如果name不爲null就把值給rename,否則rename就重新賦值

3.如果不爲空才做一件事 ?.

    var addr:String?="深圳"
    //addr不爲空才執行let中的任務
    addr?.let {
        println("to do something")
    }

4.函數返值可以爲空

fun request():Response?{
    return null
}
class Response

2.等號操作符

相等,在 kotlin 中有兩種相等:

1).===表示: 參照相等(指向相同的對象):內存單元相同
2).== 表示: 結構相等

1).參照相等

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

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

a.identityEquals(b)
a identityEquals b

2).結構相等

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

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

如果 a 不是 null 則調用 equals(Any?) 函數,否則檢查 b 是否參照等於 null

注意完全沒有必要爲優化你的代碼而將 a == null 寫成 a === null 編譯器會自動幫你做的。

注意: 上面兩種等號,字面意思有所不同,但實際效果沒有區別.

    var x1 ="a"
    var x2 ="a"
    println(x1 == x2)
    println(x1 === x2)
    println(x1.equals(x2))
    //結果都是輸出 true

3. in 和downTo

for (i in 20 downTo 15) 
{
     println(i)
}

for (i in 1..10) {
     println(i)
}

第三章 控制流

1.IF表達式

在Kotlin中, 沒有三元表達式,傳統用法:

var max = a 
if (a < b) 
  max = b 

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

IF 作爲表達式的用法:

var a:Int = 1
var b:Int = 2
val max:Int = if (a > b) a else b
val min = if (a > b) a else b

IF作爲返回值的用法:

 fun asRetunExpression1(a:Int,b:Int):Int =  if (a > b) a else b

 fun asRetunExpression2(a:Int,b:Int):Int {
     return  if (a > b) a else b
 }
 fun asRetunExpression3(a:Int,b:Int):Int {
    if (a > b) return a else return b
 }

2.When表達式

替代Switch的用法

var x = 1
when (x) {
  1 -> print("x == 1")
  2 -> print("x == 2")
  else -> { // Note the block
    print("x is neither 1 nor 2")
  }
}

常規的When用法

var x ="2"
var i = 2
when (i) {
   parseInt(x) -> print("s encodes x")
   else -> print("s does not encode x")
}

我們可以在判斷分支條件的地方使用任何表達式,而不僅僅是常量(和switch不同):

var x = 3
var validNumbers = 100..200
when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

in , !in在When中的使用

when (x) {
  in 1..10 -> print("x is in the range")
  in validNumbers -> print("x is valid")
  !in 10..20 -> print("x is outside the range")
  else -> print("none of the above")
}

有參數When,可以用 is來檢測類型

val hasPrefix = when(x) {
  is String -> x.startsWith("prefix")
  else -> false
}

無參數When的使用

when {
  x.isOdd() -> print("x is odd")
  x.isEven() -> print("x is even")
  else -> print("x is funny")
}

查看 grammar for when{: .keyword }.

3.For循環

For循環主要有以下用法

//輸出一個範圍內的值
fun forExample1() {

    for (i in 1..10) {
        println(i)
    }

}
//從20輸出到位15
fun forExample2() {
    for (i in 20 downTo 15) {
        println(i)
    }
}

//輸出集全元素
fun forExample3() {
    val arrayInts = arrayListOf("one", "two", "three")
    for ((i, item) in arrayInts.withIndex()) {
        println("index = $i, item value is = $item")
    }
}
//in關鍵字在表達式中的用法
fun forExample4() {
    var char = 'A'
    var result = char in 'A'..'Z' || char in 'a'..'z'
    println(result)
}

4.While循環

while{: .keyword } 和 do{: .keyword }..while{: .keyword } 的使用方法和其他語言一致

while (x > 0) {
  x--
}
do {
  val y = retrieveData()
} while (y != null) // y is visible here!

第四章 類和繼承

1.類

類聲明Kotlin使用關鍵字class

class Invoice {}
或者
clas Invoice

這個類聲明被花括號包圍,包括類名、類頭(指定其類型參數,主構造函數等)和這個類的主幹。類頭和主幹都是可選的; 如果這個類沒有主幹,花括號可以被省略。

class Empty

2.構造

在Kotlin中的類可以有主構造函數和一個或多個二級構造函數。主構造函數是類頭的一部分:它跟在這個類名後面(和可選的類型參數)

class Person constructor(firstName: String) {
}

如果這個主構造函數沒有任何註解或者可見的修飾符,這個constructor{: .keyword }關鍵字可以被省略

class Person(firstName: String) {
}

這個主構造函數不能包含任何的代碼。初始化的代碼可以被放置在initializer blocks(初始的模塊),以init爲前綴作爲關鍵字{:.keyword}

class Customer(name: String) {
    init {
        logger.info("Customer initialized with value ${name}")
    }
}

請注意,主構造的參數可以在初始化模塊中使用。它們也可以在類體內聲明初始化的屬性:

class Customer(name: String) {
    val customerKey = name.toUpperCase()
}

事實上,聲明屬性和初始化主構造函數,Kotlin有簡潔的語法:

class Person(val firstName: String, val lastName: String, var age: Int) {
  // ...
}

與普通屬性一樣,主構造函數中聲明的屬性可以是可變的或者是隻讀的

If the constructor has annotations or visibility modifiers, the constructor{: .keyword } keyword is required, and the modifiers go before it: 如果構造函數有註解或可見性修飾符,這個constructor{: .keyword }需要被關鍵字修飾。

class Customer public inject constructor(name: String) { ... }

3.擴展構造函數

類也可以擁有被稱爲”二級構造函數”(爲了實現Kotlin向Java一樣擁有多個構造函數),通常被加上前綴”constructor”

class Person {
    constructor(parent: Person) {
        parent.children.add(this)
    }
}

如果類有一個主構造函數,每個二級構造函數需要委託給主構造函數,直接或間接地通過另一個二級函數。 委託到另一個使用同一個類的構造函數用this關鍵字

class Person(val name: String) {
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

如果一個非抽象類沒有聲明任何構造函數(原發性或繼發性),這將有一個生成的主構造函數不帶參數。構造函數的可見性是public。

如果你不希望你的類有一個公共構造函數,你需要聲明與非缺省可見一個空的主構造函數:

class DontCreateMe private constructor () {
}

注意在JVM上,如果所有的主構造函數的參數有默認值,編譯器會產生一個額外的參數的構造函數,將使用默認值。

class Customer(val customerName: String = "")

4.創建類的實例

要創建一個類的實例,我們調用構造函數,就好像它是普通的函數:

val invoice = Invoice()

val customer = Customer("Joe Smith")

注意Kotlin不能有“new”關鍵字

5.類成員

類可以包括

  • 構造和初始化模塊
  • 函數
  • 屬性
  • 匿名和內部類
  • 對象聲明
  • 繼承
  • 伴隨對象

在Kotlin所有的類中都有一個共同的父類Any,這是一個默認的父類且沒有父類型聲明:

class Example // Implicitly inherits from Any

Any不屬於java.lang.Object;特別是,它並沒有任何其他任何成員,甚至連equals(),hashCode()和toString()都沒有。

要聲明一個明確的父類,我們把類型放到類頭冒號之後:

open class Base(p: Int)

class Derived(p: Int) : Base(p)

如上所見,父類可以(並且必須)在聲明繼承的地方,用原始構造函數初始化。

如果類沒有主構造,那麼每個次級構造函數初始化基本類型 使用super關鍵字,或委託給另一個構造函數做到這一點。 注意,在這種情況下,不同的二級構造函數可以調用基類型的不同的構造:

class MyView : View {
    constructor(ctx: Context) : super(ctx) {
    }

    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) {
    }
}

父類上的open標註可以理解爲Java中final的反面,它允許其他他類 從這個類中繼承。
默認情況下,在Kotlin所有的類都是final,.

對應於 Effective Java 書中的17條:設計並顯示標註繼承,否則就禁止它。

伴隨對象實現單例,靜態變量的效果

class Student(name: String) {

    var name: String? = null
    init {
        this.name = name
    }

    companion object {
         @Volatile private var student: Student?= null
        fun getSingleton():Student? {
            if (this.student == null) {
                synchronized(this) {
                    this.student = Student("文忠湖")
                }
            }
            return this.student
        }
    }

    fun walk(){
        println("walk")
    }
}

fun main(args:Array<String>){
    var student:Student? = Student.getSingleton()
    student?.walk()
}

6.覆蓋成員

我們之前提到過,Kotlin力求清晰顯式。不像Java中,Kotlin需要明確的 標註覆蓋的成員(我們稱之爲open)和重寫的函數。(繼承父類並覆蓋父類函數時,Kotlin要求父類必須有open標註,被覆蓋的函數必須有open標註,並且子類的函數必須加override標註。):

open class Base {
  open fun v() {}
  fun nv() {}
}
class Derived() : Base() {
  override fun v() {}
}

Derived.v()函數上必須加上override標註。如果沒寫,編譯器將會報錯。 如果父類的這個函數沒有標註open,則子類中不允許定義同名函數,不論加不加override。 在一個final類中(即沒有聲明open的類),函數上也不允許加open標註。

成員標記爲override 的本身是開放的,也就是說,它可以在子類中重寫。如果你想禁止重寫的,使用final 關鍵字:

open class AnotherDerived() : Base() {
  final override fun v() {}
}

在Kotlin中,實現繼承的調用通過以下規則:
如果一個類繼承父類成員的多種實現方法,可以直接在子類中引用, 它必須重寫這個成員,並提供其自己的實現(當然也可以使用父類的)。

爲了表示從中繼承的實現而採取的父類型,我們使用super 在尖括號,如規範的父名super:

open class A {
  open fun f() { print("A") }
  fun a() { print("a") }
}

interface B {
  fun f() { print("B") } // interface members are 'open' by default
  fun b() { print("b") }
}

class C() : A(), B {
  // The compiler requires f() to be overridden:
  override fun f() {
    super<A>.f() // call to A.f()
    super<B>.f() // call to B.f()
  }
}

類C同時繼承A和B是可以的,而且我們在調用a()和b()函數時沒有任何問題,因爲他們在C的基類中只有一個實現。 但是f()函數則在A,B中都有實現,所以我們必須在C中覆蓋f(),並且提供我們的實現以消除歧義。

抽象類

類和其中的某些實現可以聲明爲abstract{:.keyword}。 抽象成員在本類中可以不用實現。。 因此,當一些子類繼承一個抽象的成員,它並不算是一個實現:

abstract class A {
  abstract fun f()
}

interface B {
  open fun f() { print("B") }
}

class C() : A(), B {
  // We are not required to override f()
}

需要注意的是,我們並不需要標註一個抽象類或者函數爲open

我們可以重寫一個open非抽象成員使之爲抽象的。

open class Base {
  open fun f() {}
}

abstract class Derived : Base() {
  override abstract fun f()
}

第五章 屬性和字段

1.聲明屬性

Kotlin的屬性. 這些聲明是可變的,用關鍵字var 或者使用只讀關鍵字val .

public class Address { 
  public var name: String = ...
  public var street: String = ...
  public var city: String = ...
  public var state: String? = ...
  public var zip: String = ...
}

要使用一個屬性,只需要使用名稱引用即可,就相當於Java中的公共字段:

fun copyAddress(address: Address): Address {
  val result = Address() // there's no 'new' keyword in Kotlin
  result.name = address.name // accessors are called
  result.street = address.street
  // ...
  return result
}

2.延遲屬性

Kotlin聲明一個屬性一定要初始化, 當你不想初始化的時候就可以用lazy關鍵字來處理了

val lazyValue: String by lazy {
    println("computed!")  //第一次初始化,會輸出這一行內容
    "Hello"
}

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

3.getters和setters

聲明一個屬性的完整語法

var <propertyName>: <PropertyType> [= <property_initializer>]
  <getter>
  <setter>

上面的定義中,初始器(initializer)、getter和setter都是可選的。 屬性類型(PropertyType)如果可以從初始器或者父類中推導出來,也可以省略。

例如:

var allByDefault: Int? // error: explicit initializer required, default getter and setter implied
var initialized = 1 // has type Int, default getter and setter

注意公有的API(即public和protected)的屬性,類型是不做推導的。 這麼設計是爲了防止改變初始化器時不小心改變了公有API。比如:

public val example = 1 // error: a public property must have a type specified explicitly

一個只讀屬性的語法和一個可變的語法有兩方面的不同:
- 1·只讀屬性的用val開始代替var
- 2·只讀屬性不許setter

val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter

我們可以編寫自定義的訪問器,非常像普通函數,對內部屬性聲明。這裏有一個定義的getter的例子:

val isEmpty: Boolean
  get() = this.size == 0

一個定義setter的例子:

var stringRepresentation: String
  get() = this.toString()
  set(value) {
    setDataFromString(value) // parses the string and assigns values to other properties
  }

按照慣例,setter參數的名稱是“value”,但是如果你喜歡你可以選擇一個不同的名稱。

如果你需要改變一個訪問器或註釋的可見性,但是不需要改變默認的實現, 您可以定義訪問器而不定義它的實例:

var setterVisibility: String = "abc" // Initializer required, not a nullable type
private set // the setter is private and has the default implementation

var setterWithAnnotation: Any?
  @Inject set // annotate the setter with Inject

第六章 接口

Kotlin 的接口很像 java 8。它們都可以包含抽象方法,以及方法的實現。和抽象類不同的是,接口不能保存狀態。可以有屬性但必須是抽象的。

接口是通過關鍵字 interface 來定義的:

interface MyInterface {
    fun bar()
    fun foo() {
    //函數體是可選的-和java8類似
    }
}

1.接口的實現

一個類或對象可以實現一個或多個接口

class Child : MyInterface {
    fun bar () {
        //函數體
    }
}
interface MyInterface {
    val property: Int //抽象屬性
    fun foo() {
        print(property)
    }
}

class Child : MyInterface {
    override val property: Int = 29
}

2.抽象類

用關鍵字abstract 定義抽象類 ,抽象方法也需要用abstract .


interface Biology{
    fun walk()
    fun eat()
}
abstract class Animal(name: String, age: Int):Biology {

    open fun sleep(){
        println("sleep()")
    }

    abstract fun laugh()
}

3.繼承

注意構造方法的傳參,Kotlin也只支持單繼承.

class Pig(name:String,age:Int,sex:String): Animal(name,age,sex){
    override fun laugh() {
        println(" $name 都笑了.")
    }

    override fun walk() {
        println("$sex $name  $age years old : walk. ")
    }

    override fun eat() {
        println("$sex $name $age years old : eat.")
    }

    override fun sleep() {
        println("$sex $name $age years old : sleep.")
    }
}

4.解決衝突

當我們在父類中聲明瞭許多類型,有可能出現一個方法的多種實現。比如:

interdace A {
    fun foo() { print("A") }
    fun bar()
}

interface B {
    fun foo() { print("B") }
    fun bar() { print("bar") }
}

class C : A {
    override fun bar() { print("bar") }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }
}

A B 接口都有聲明瞭 foo() bar() 函數。它們都實現了 foo() 方法,但只有 B 實現了 bar() ,bar() 在 A 中並沒有聲明它是抽象的,這是因爲在接口中如果函數沒有函數體,那麼默認是抽像的。

現在,如果我們從 A 中派生一個 C 實體類,顯然我們需要重寫 bar() ,並實現它。而我們從 A 和 B 派生一個 D ,我們不用重寫 bar() 方法,因爲我們的一個繼承中有一個已經實現了它。

但我們繼承了倆個 foo() 的實現,因此編譯器不知道應該選哪個,並強制我們重寫 foo() 並且明確指出我們想怎麼實現。

第七章 函數與屬性擴展

Kotlin 與 C# Gosu 類似,提供了不用從父類繼承,或者使用像裝飾模式這樣的設計模式來給某個類進行擴展。這是通過叫擴展的特殊聲明來達到的。現在, Kotlin 支持擴展函數和屬性。

1.擴展函數

聲明一個擴展函數,我們需要添加一個接收者類型的的前綴。
下面是給MutableList添加一個 swap 函數的例子:

fun MutableList<Int>.swap(x: Int, y: Int) {
    val temp = this[x] // this 對應 list
    this[x] = this[y]
    this[y] = tmp
}

在擴展函數中的 this 關鍵字對應接收者對象。現在我們可以在任何 MutableList 中使用這個函數了:

fun <T> MutableList<T>.swap(x: Int, y: Int) {
  val tmp = this[x] // 'this' corresponds to the list
  this[x] = this[y]
  this[y] = tmp
}

2.可空的接受者

值得注意的是擴展可以定義一個可空的接受者。這樣的擴展可以算作對象的變量,即使它是空的,你可以在函數體內檢查 this == null 。這樣你就可以在 Kotlin 中不進行空檢查就可以調用 toString() 方法:這樣的檢查是在擴展函數中做的。

fun Any?.toString(): String {
    if (this == null) return "null"
    return toString()
}

3.擴展屬性

和函數類似, Kotlin 也支持屬性擴展:

val <T> List<T>.lastIndex:  Int
    get() = size-1

注意,擴展並不是真正給類添加了成員,沒有比給擴展添加備用字段更有效的辦法了/這就是爲什麼初始化函數是不允許擴展屬性。它們只能通過明確提供 getter setter 來作用。

4.伴隨對象擴展

如果一個對象定義了伴隨對象,你也可以給伴隨對象添加擴展函數或擴展屬性:

class MyClass {
    companion object {} 
}
fun Myclass.Companion.foo() {

}

和普通伴隨對象的成員一樣,它們可以只用類的名字就調用:

MyClass.foo()

5.擴展的範圍

大多數時候我們在 top level 定義擴展,就在包下面直接定義:

package foo.bar
fun Baz.goo() { ... }

在聲明的包外面使用這樣的擴展,我們需要在 import 時導入:

package com.example,usage
import foo.bar.goo//導入所有名字叫 "goo" 的擴展
import foo.bar.*
fun usage(baz: Baz) {
    baz.goo()
}

6.可變參數Varargs

fun request(vararg  params:String){
    for( (i,item) in params.withIndex() ){
        println("$i,$item")
    }
}
fun main(args:Array<String>){
    request("fruit","pig","plant")
}

第八章 數據類

我們經常創建一些只是處理數據的類。在這些類裏的功能經常是衍生自他們所持有的數據。在Kotlin中,這樣的類可以被稱爲data:

data class User(val name: String, val age: Int)

這被叫做一個 數據類。編譯器自動從在主構造函數定義的全部特性中得到以下成員:

equals()/hashCode(),
toString() 格式是 "User(name=John, age=42)",
componentN() functions 對應按聲明順序出現的所有屬性,
copy() 方法 .

如果有某個函數被明確地定義在類裏或者被繼承,編譯器就不會生成這個函數。

NOTE如果一個構造參數沒有val或者var在前面,它將不會被包括進這些成員裏;也不會在類裏聲明成屬性或者繼承自父類

在JVM中,如果生成的類需要含有一個無參的構造函數,則所有的屬性必須有默認值。

data class User(val name: String = "", val age: Int = 0)

1.複製

在很多情況下,我們我們需要對一些屬性做修改而其他的不變。這就是copy()這個方法的來源。對於上文的User類,應該是這麼實現這個方法的

fun copy(name: String = this.name, age: Int = this.age) = User(name, age)     

也可以這麼寫

val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

2.數據類和多重聲明

成員方法用於使數據類可以多聲明:

val jane = User("Jane", 35) 
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"

3.標準數據類

在標準庫提供了Pair和Triple。在很多情況下,即使命名數據類是一個更好的設計選擇,因爲這能讓代碼可讀性更強。

第九章 內部類

1.靜態內部類

不可以訪外部類的屬性

class Outer {
  private val bar: Int = 1
  class Nested {
    fun foo() = 2
  }
}

val nested = Outer.Nested()
nested.foo()

2.非靜態內部類

加上Inner關鍵字,就是非靜態內部類. 內部類會帶有一個來自外部類的對象的引用,且可以訪問外部類的屬性方法.

class Outer {
  private val bar: Int = 1
  inner class Inner {
    fun foo() = bar
  }
}

val inner= Outer.Inner()
inner.foo()

第十章 安全構建

請參考官網: 

http://kotlinlang.org/docs/reference/type-safe-builders.html#full-definition-of-the-comexamplehtml-package

第十一章 其它

1.異常

1). try作爲表達式

fun tryException(){
    var result = try{
        Integer.parseInt("2"); //如果轉換成功,結果會賦值給result
    }catch (a: Exception){
        null  //如果轉換失敗,null會賦值給result
    }finally{
        println("not as a result") // 這裏不會作爲結果賦值給result
    }
    println(result)  
}

2).自定義異常
類似於java一樣的寫法, 實際上它就是調用的java的異常基類

class ExceptionExtend(message:String):Exception(message){

}

我們可以查看Kotlin源碼,他作了一個映射, Kotlin映射java裏面的類

@SinceKotlin("1.1") public typealias Error = java.lang.Error
@SinceKotlin("1.1") public typealias Exception = java.lang.Exception

2.反射

直接看例子, 網上所有教程都只對 ::簡單說明了一下,在這裏給出例子更容易懂.

fun main(args:Array<String>){
    var tmp = Teacher()
    println(tmp::class)
    println(tmp::class.simpleName)
    println(tmp::class.qualifiedName)

    var members = tmp::class.members
    for( (i,item) in members.withIndex()){
        if(item.name == "walk")
         item.call(tmp) //類似java的invoke方法.
    }

}

class Student{
    var name:String?="student"
    var age:Int=1

    fun walk(){
        println("walk")
    }
}

3.對象解構

下面代碼不能正常運行,暫時找不到足夠資料解,先當作瞭解.

    var teacher = Teacher("習近平",50)
    var (name:String?,age:Int) = teacher

4.this表達式

這是官網給出的例子:

class A { // implicit label @A
    inner class B { // implicit label @B
        fun Int.foo() { // implicit label @foo
            val a = this@A // A's this
            val b = this@B // B's this
            val c = this // foo()'s receiver, an Int
            val c1 = this@foo // foo()'s receiver, an Int

            val funLit = lambda@ fun String.() {
                val d = this // funLit's receiver
            }
            val funLit2 = { s: String ->
                // foo()'s receiver, since enclosing lambda expression
                // doesn't have any receiver
                val d1 = this
            }
        }
    }
}

5.類的代理

使用by關鍵代理一個類, 它是替換繼承的一個不錯方案.

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}
// 一定要是接口纔可以被代理, 這裏的Base是接口.
class Derived(b: Base) : Base by b  

fun main(args: Array<String>) {
    val b = BaseImpl(10)
    Derived(b).print() // prints 10
}

7.屬性代理

有一些常見的屬性類型,雖然我們可以在每次需要的時候手動實現它們, 但是如果能夠爲大
家把他們只實現一次並放入一個庫會更好。例如包括
- 延遲屬性(lazy properties): 其值只在首次訪問時計算,
- 可觀察屬性(observable properties): 監聽器會收到有關此屬性變更的通知,
- 把多個屬性儲存在一個映射(map)中,而不是每個存在單獨的字段中。

爲了涵蓋這些(以及其他)情況,Kotlin 支持 委託屬性:

class Example {
var p: String by Delegate()
}

屬性代理不必實現任何的接口,但是需要提供一個 getValue() 函數(和 setValue()

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name} in $thisRef.'")
    }
}

當我們從委託到一個 Delegate 實例的 p 讀取時,將調用 Delegate 中的 getValue() 函
數, 所以它第一個參數是讀出 p 的對象、第二個參數保存了對 p 自身的描述.

可觀察屬性 Observable
Delegates.observable() 它有三個 參數:被賦值的屬性、舊值和新值:

import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<default>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "first"
user.name = "second"
}
//這個例子輸出:
<default> -> first
first -> second

如果你想能夠截獲一個賦值並“否決”它,就使用 vetoable() 取代 observable() 。 在屬性被
賦新值生效之前會調用傳遞給 vetoable 的處理程序。

import kotlin.properties.Delegates
class User {
    var name: String by Delegates.vetoable(""){
        prop, old, new ->
        if(new =="not allow value")
            false
        else 
            true
    }
}

把屬性儲存在映射中
一個常見的用例是在一個映射(map)裏存儲屬性的值。 這經常出現在像解析 JSON 或者做
其他“動態”事情的應用中。 在這種情況下,你可以使用映射實例自身作爲委託來實現委託屬
性。

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int by map
}

在這個例子中,構造函數接受一個映射參數:

    val user = User(mapOf(
        "name" to "John Doe",
        "age" to 25
    ))

委託屬性會從這個映射中取值(通過字符串鍵——屬性的名稱):

println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
//這也適用於 var 屬性,如果把只讀的 Map 換成 MutableMap 的話:

class MutableUser(val map: MutableMap<String, Any?>) {
    var name: String by map
    var age: Int by map
}

8.註解

    annotation class Document

9.別名

1).當一個類型特別長的時候,就非常有用.

typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>

2).你也可以爲函數起別名

typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean

3).也可以爲內部類起別名

class A {
    inner class Inner
}
class B {
    inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner
發佈了45 篇原創文章 · 獲贊 9 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章