kotlin手冊(cè)

kotlin 手冊(cè)

class類的使用

java中的類聲明

??
public class MainActivity extends AppCompatActivity {
    @override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
}

對(duì)應(yīng)kotlin的寫法就是:

???
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
    }
}

有這么幾點(diǎn)不同:

  1. Java中的public在kotlin中可以省略, kotlin的類默認(rèn)是public的.
  2. 類的繼承的寫法, Java里用的是extends, 而在kotlin里使用:, 但其實(shí) : 不僅可以表示繼承某個(gè)類, 還可以表示Java中的implement 某個(gè)接口.
  3. kotlin把構(gòu)造函數(shù)單獨(dú)用了一個(gè)constructor關(guān)鍵字來和其他的fun做區(qū)分.
  4. java里面@override是注解的形式, kotlin里的override變成了關(guān)鍵字.
  5. kotlin里的類默認(rèn)是final的, 也就是不能被其他類去繼承,要想讓這個(gè)類可以被繼承, 需要加上open關(guān)鍵字, 下面這樣的MainActivity就可以被繼承了.
  6. kotlin里創(chuàng)建類實(shí)例的時(shí)候, 不需要new關(guān)鍵字.
  7. kotlin定義接口使用interface關(guān)鍵字.
???
open class MainActivity : AppCompatActivity() {

}

在繼承類的時(shí)候, 對(duì)被繼承類后的()的理解.

???
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
    }
}

上面這種方式就相當(dāng)于給MainActivity類聲明了一個(gè)無參的構(gòu)造方法, 是一種簡(jiǎn)單的寫法, 它就等價(jià)于下面這樣:

???
//注意這里AppCompatActivity后面沒有'()'
class MainActivity : AppCompatActivity {
    constructor() {
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
    }
}
構(gòu)造器Constructor

java里

public class User {
    int id;
    String name;
    
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

kotlin里

class User {
    val id: Int
    val name: String
    
    constructor(id: Int, name: String) {
        //上面這里沒有public
        this.id = id
        this.name = name
    }
}

兩點(diǎn)不同:

  1. java中的構(gòu)造器和類同名, kotlin中使用constructor關(guān)鍵字表示.
  2. kotlin中構(gòu)造器沒有public修飾, 因?yàn)槟J(rèn)可見性就是公開的.
主構(gòu)造器和次構(gòu)造器的概念

寫在類名后面的構(gòu)造器叫做主構(gòu)造器, 寫在類中的構(gòu)造器被稱為次構(gòu)造器.

class User constructor(name: String) {
    //下面的name就是上面構(gòu)造器的入?yún)ame
    var name: String = name
}

主構(gòu)造器有3個(gè)特點(diǎn):

  1. constructor構(gòu)造器移到了類名后面
  2. 類的成員變量name可以引用主構(gòu)造器中的參數(shù)name的值
  3. kotlin中, 一個(gè)類最多只能有1個(gè)主構(gòu)造器(也可以沒有), 而次構(gòu)造器的個(gè)數(shù)是沒有限制的.

主構(gòu)造器中的參數(shù)除了可以在類的成員變量中使用外, 還可以在init代碼塊中使用.


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

其中, init代碼塊是緊跟在主構(gòu)造器之后執(zhí)行的, 這是因?yàn)橹鳂?gòu)造器本身沒有代碼體, init代碼塊就充當(dāng)了主構(gòu)造器代碼體的功能.
另外, 如果類中有主構(gòu)造器, 那么其他的次構(gòu)造器都需要通過this關(guān)鍵字調(diào)用主構(gòu)造器, 可以直接調(diào)用或者是通過別的次構(gòu)造器間接調(diào)用, 如果不調(diào)用, IDE就會(huì)報(bào)錯(cuò):

class User constructor(var name: String) {
    constructor(name: String, id: Int) {
        //這里寫就會(huì)報(bào)錯(cuò), Primary constructor call expected.
    }   
}

當(dāng)一個(gè)類同時(shí)有主構(gòu)造器和次構(gòu)造器的時(shí)候, 需要這樣寫:

class User constructor(var name: String) {
    //直接調(diào)用主構(gòu)造器
    constructor(name: String, id: Int) : this(name) {
        ...
    }
    
    //通過上一個(gè)次構(gòu)造器, 間接調(diào)用主構(gòu)造器.
    constructor(name: String, id: Int, age: Int) : this(name, id) {
        ...
    }


}

通常情況下, 主構(gòu)造器中的constructor關(guān)鍵字可以省略.

class User(name: String) {
    var name: String = name
}

還有一個(gè)特別的用法, 可以在主構(gòu)造器里用var關(guān)鍵字來直接快速的給類聲明出N個(gè)同名的成員變量.

class User(var name: String) {
    ...
}

等價(jià)于:

class User(name: String) {
    var name: String = name
}

如果在主構(gòu)造的參數(shù)聲明時(shí)加上var或者val, 就等價(jià)于在類中創(chuàng)建了該名稱的屬性(property), 并且初始值就是主構(gòu)造器中的該參數(shù)的值.

init關(guān)鍵字, 用于初始化代碼塊

在java中

public class User {
    {
        //初始化代碼塊, 先于下面的構(gòu)造方法執(zhí)行
    }
    public User() {
    }


}

在kotlin中

class User {
    init {
        //初始化代碼塊, 先于下面的構(gòu)造方法執(zhí)行
    }
    
    constructor() {
        ...
    }

}


靜態(tài)類, 靜態(tài)方法和靜態(tài)成員變量

就是使用object關(guān)鍵字

object Sample {
    val name = "a name"
}

它的意思很直接: 創(chuàng)建一個(gè)類, 并且創(chuàng)建一個(gè)這個(gè)類的對(duì)象, 這個(gè)就是object的意思: 對(duì)象.
在代碼中如果要使用這個(gè)對(duì)象, 直接通過它的類名就可以訪問:

Sample.name

這不就是單例么, 所以在kotlin中創(chuàng)建單例不用像java中那么復(fù)雜, 只需要把class換成object就可以了.
java中:

public class A {
    private static A sInstance;
    
    public static A getInstance() {
        if (sInstance == null) {
            sInstance = new A();
        }
        return sInstance;
    }

}

在kotlin中就可以很簡(jiǎn)單的這樣寫:

//class 換成了object
object A {
    val number: Int = 1
    fun method() {
        println("A.method()")
    }
}

如果想讓上面的method()靜態(tài)方法在java文件中調(diào)用, 那就在method()方法前加上注解, @JvmStatic

用object修飾的對(duì)象中的變量和函數(shù)都是靜態(tài)的, 但有時(shí)候, 我們只想讓類中的一部分函數(shù)和變量是靜態(tài)的該怎么做呢.
這時(shí)候就可以使用companion object.

class A {
    companion object {
        var c: Int = 0
    }

}

也就是說java中的靜態(tài)變量和靜態(tài)方法的等價(jià)寫法是: companion object 變量和函數(shù).

top-level property / function 聲明

其實(shí)就是把屬性和函數(shù)的聲明不寫在class里面, 這個(gè)在kotlin里是準(zhǔn)許的.

package com.hencoder.plus
// 屬于package, 不在class/object內(nèi)

fun topLevelFunction() {
}

上面這些工具方法就可以定義為頂層方法, 比如dp2dx()這樣的方法.這樣IDE就可以通過代碼提示快速讓開發(fā)人員找到合適的方法.

常量的聲明 const 關(guān)鍵字
class Sample {
    companion object {
        const val CONST_NUMBER = 1
    }

}

const val CONST_SECOND_NUMBER = 2

就記住一點(diǎn)就行, kotlin中只有基本類型和String類型可以聲明為常量.

is 和 as 關(guān)鍵字

is關(guān)鍵字等同于java中的instanceOf關(guān)鍵字.
as關(guān)鍵字用于把一個(gè)類強(qiáng)制轉(zhuǎn)換為它的某個(gè)子類.

fun main() {
    var activity: Activity = NewActivity()
    (activity as NewActivity).action()
}

但上面這樣做, 如果強(qiáng)轉(zhuǎn)成了一個(gè)錯(cuò)誤的類型, 就會(huì)拋出異常.
這時(shí)候我們可以使用 as? 來解決.

fun main() {
    var activity: Activity = NewActivity()
    //'(activity as? NewActivity)' 之后是一個(gè)可空類型的對(duì)象, 所以, 需要使用?.來調(diào)用.
    (activity as? NewActivity)?.action()
}

它的意思就是說如果強(qiáng)轉(zhuǎn)成功就執(zhí)行之后的調(diào)用, 如果強(qiáng)轉(zhuǎn)不成功就不執(zhí)行后面的調(diào)用.

by 關(guān)鍵字

在聲明一個(gè)類的時(shí)候, 這個(gè)類可能要實(shí)現(xiàn)某個(gè)接口, 這時(shí)候在接口的右邊寫上by關(guān)鍵字.所起的功能就是把類對(duì)這個(gè)接口的實(shí)現(xiàn), 委托給了指定的對(duì)象.
當(dāng)你想讓某個(gè)類實(shí)現(xiàn)某個(gè)接口, 但不想關(guān)心這個(gè)接口的大部分實(shí)現(xiàn)方法, 只想給它做一些功能擴(kuò)展, 那么就用by關(guān)鍵字來個(gè)接口委托, 讓這個(gè)接口的大部分實(shí)現(xiàn)方法由指定對(duì)象來進(jìn)行插件式提供.

還有一種用法是對(duì)這個(gè)接口中的某個(gè)方法單獨(dú)進(jìn)行定制修改, 這樣這個(gè)類的對(duì)象對(duì)這個(gè)接口的實(shí)現(xiàn)就以新定制的方法實(shí)現(xiàn)為準(zhǔn), 就不交給委托對(duì)象了, 也就是說委托對(duì)象對(duì)這個(gè)接口的某個(gè)實(shí)現(xiàn)就被廢除了.

這里有2個(gè)典型的使用場(chǎng)景.
比如要實(shí)現(xiàn)一個(gè)用戶列表的類, 它需要實(shí)現(xiàn)List接口中的方法, 例如get(), set(), remove()之類的. 但除了這些通用的方法外, 還需要新增加兩個(gè)定制方法, 一個(gè)是查找高風(fēng)險(xiǎn)用戶的列表, 另一個(gè)是按照年齡對(duì)列表進(jìn)行排序.
這時(shí)候就可以用到接口委托的這種方法來處理.

//注意: by關(guān)鍵字的后面是一個(gè)對(duì)象.
class UserList(private val list: List<User>) : List<User> by list {
    fun highRiskUsers() : List<User> {...}
    fun sortWithAge() {...}
}

//在創(chuàng)建這個(gè)類對(duì)象的時(shí)候, 入?yún)ist就是by后面的list對(duì)象.

當(dāng)然, 這就要求在構(gòu)造UserList的時(shí)候, 傳入一個(gè)實(shí)現(xiàn)了List<User>接口的對(duì)象進(jìn)來.

例如, 下面這個(gè)demo code

// 創(chuàng)建接口
interface Base {
    fun print()
}

// 實(shí)現(xiàn)此接口的被委托的類
class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

// 通過by關(guān)鍵字創(chuàng)建委托類
class Derived(b: Base) : Base by b

//使用的方法
fun main(args: Array<String>) {
    val b = BaseImpl(10)
    Derived(b).print() //這里輸出結(jié)果是10
}

在sdk中有不少這樣的使用案例.


image.png
用data class 定義數(shù)據(jù)類

在定義類的時(shí)候, 加上data關(guān)鍵字, 這樣就定義出一個(gè)數(shù)據(jù)類, 用于專門保存數(shù)據(jù)使用. 好處是它會(huì)自動(dòng)為這個(gè)類實(shí)現(xiàn)toString(), equals(), hashCode()這些方法, 這樣在直接打印這個(gè)類的對(duì)象的時(shí)候, 就不會(huì)只打印對(duì)象的地址, 而是根據(jù)類的成員變量的值, 打印這個(gè)對(duì)象的內(nèi)容. 同時(shí), 使用data class也可以讓代碼的可讀性更好一些.

data class Person(val name: String = "Android") {
  var age: Int = 0
}
如何寫一個(gè)新類, 繼承自某個(gè)已經(jīng)存在的類

例如, SqAlertDialog的構(gòu)造方法如下:

  protected SqAlertDialog(Context context) {
      super(context, R.style.NoTitleDialog);
      mContext = context;
  }

那么寫一個(gè)繼承自這個(gè)類的聲明就應(yīng)該是下面這樣的.

class UpdateReminderGuideDialog(context: Context) : SqAlertDialog(context) {

}
enum類的使用

這塊內(nèi)容還沒有總結(jié), 后面再補(bǔ)充. ahking17.

方法(函數(shù))

函數(shù)的聲明

以fun關(guān)鍵字開頭.
返回值的類型寫在函數(shù)和參數(shù)的后面.

fun cook(name: String) : Food {
  ...
}

如果沒有返回值該怎么辦? Java里是返回void, kotlin里是返回Unit, 并且可以省略.

函數(shù)的簡(jiǎn)化寫法, 使用 = 連接返回值

fun area(width: Int, height: Int): Int {
    return width * height
}

這種只有一行代碼的函數(shù), 可以簡(jiǎn)化寫成.

fun area(width: Int, height: Int): Int = width * height

以上是函數(shù)有返回值時(shí)的情況, 對(duì)于沒有返回值的情況, 可以理解為返回值是Unit.

fun sayHi(name: String) {
  println("Hi " + name)
}

可以簡(jiǎn)化成下面這樣:

fun sayHi(name: String) = println("Hi " + name)

函數(shù)的參數(shù)支持有默認(rèn)值

fun sayHi(name: String = "world") = println("Hi " + name)

這就等價(jià)于用Java寫的重載方法, 當(dāng)調(diào)用sayHi函數(shù)時(shí), 參數(shù)是可選的.

sayHi("kaixue.io")
sayHi() //使用了默認(rèn)值 "world"

使用函數(shù)的命名參數(shù)來調(diào)用函數(shù)

fun sayHi(name: String = "world", age: Int) {
  ...
}

sayHi(age = 21)

這種顯式地指定了參數(shù)的名字和參數(shù)值得方式, 就叫做命名參數(shù).
kotlin中的每一個(gè)函數(shù)參數(shù)都可以作為命名參數(shù).
再來看一個(gè)有非常多參數(shù)的函數(shù)的例子:

fun sayHi(name: String = "world", age: Int, isStudent: Boolean = true, isFat: Boolean = true, isTall: Boolean = true) {
    ...
}

當(dāng)函數(shù)中有非常多的參數(shù)時(shí), 調(diào)用該函數(shù)就會(huì)寫成這樣:

sayHi("world", 21, false, true, false)

上面這樣可讀性就比較差, 可以改成使用命名參數(shù)的調(diào)用方式:

sayHi(name = "world", age = 21, isStudent = false, isFat = true, isTall = false)

與之對(duì)應(yīng)的概念就是我們使用使用的所謂位置參數(shù), 但是要注意的是, 所有位置參數(shù)都應(yīng)該放在第一個(gè)命名參數(shù)之前, 不然IDE就會(huì)報(bào)編譯錯(cuò)誤.

本地函數(shù)(嵌套函數(shù)), 在函數(shù)里再定義一個(gè)函數(shù).

fun login(user: String, password: String, illegalStr: String) {
  fun validate(value: String) {
      if (value.isEmpty()) {
          throw IllegalArgumentException(illegalStr)
      }
      validate(user, illegalStr)
      validate(password, illegalStr)
  }

}

這里我們將共同的驗(yàn)證邏輯放進(jìn)了嵌套函數(shù)validate中, 并且login函數(shù)之外的其他地方無法訪問這個(gè)嵌套函數(shù).

內(nèi)置函數(shù)

let函數(shù)

使用場(chǎng)景: 方便對(duì)某個(gè)對(duì)象的多次連續(xù)調(diào)用做統(tǒng)一的判空處理

// 使用java的情況下
if (mVar != null) {
  mVar.function1();
  mVar.function2();
  mVar.function3();
}

// 使用kotlin (不使用let函數(shù)的情況下)
mVar?.function1()
mVar?.function2()
mVar?.function3()

// 使用kotlin (使用let函數(shù))
// 方便了統(tǒng)一判空的處理 & 確定了mVar變量的作用域

mVar?.let {
    it.function1()
    it.function2()
    it.function3()
}

上面的it就代表mVar對(duì)象.
這樣寫的好處是當(dāng)對(duì)一個(gè)對(duì)象連續(xù)調(diào)用它的幾個(gè)方法的時(shí)候, 用這種寫法可以省的連續(xù)去寫mVar這個(gè)對(duì)象, 代碼看起來更舒服些.

also函數(shù)

和let函數(shù)作用一樣, 區(qū)別就是返回值不同.
let函數(shù): 返回值 = 最后一行或是return的表達(dá)式, 作為返回值.
also函數(shù): 返回值 = 傳入的對(duì)象的本身.

//let函數(shù)
var result = mVar.let {
                        it.function1()
                        it.function2()
                        it.function3()
                        999
                  }
// 最終結(jié)果 = 返回999 給變量result

//also函數(shù)
var result = mVar.also {
                        it.function1()
                        it.function2()
                        it.function3()
                        999
                  }
// 最終結(jié)果 = 返回一個(gè)mVar對(duì)象給變量result
with函數(shù)

當(dāng)需要連續(xù)調(diào)用同一個(gè)對(duì)象的多個(gè)方法或是屬性時(shí), 可以省去寫對(duì)象名, 在 {} 中直接調(diào)用方法名或是屬性即可, 這樣就省的去寫對(duì)象名了. 代碼能更簡(jiǎn)潔些.

with(object) {
  // ...
}

//返回值 = 函數(shù)塊的最后一行 或是return表達(dá)式.
// 此處要調(diào)用people的name和age屬性
//kotlin用法
val people = People("carson", 25)
with(people) {
  println("my name is $name, I am $age years old")
}

//java用法
User people = new People("carson", 25);
String var1 = "my name is " + people.name + ", I am " + people.age + " years old";
System.out.println(var1);
run函數(shù)

同時(shí)結(jié)合let和with兩個(gè)函數(shù)的作用.

  • 調(diào)用同一個(gè)對(duì)象的多個(gè)方法或?qū)傩詴r(shí), 可以省去重復(fù)寫對(duì)象名, 直接調(diào)用方法名或?qū)傩约纯?
  • 統(tǒng)一做了判空處理.
object.run {
  ...
}
// 返回值 = 函數(shù)塊的最后一行 或是 return表達(dá)式

使用示例

//此處要調(diào)用people的name和age屬性, 且要判空
//kotlin
val people = People("carson", 25)
people?.run {
  println("my name is $name, I am $age years old")
}

//對(duì)比java 
User people = new People("carson", 25);
String var1 = "my name is " + people.name + ", I am " + people.age + " years old";
System.out.println(var1);
apply函數(shù)

與run函數(shù)的作用是一樣的, 區(qū)別僅在于返回值.

  • run函數(shù)的最后一行的結(jié)果是返回值
  • apply函數(shù)返回傳入的對(duì)象的本身.
    示例:
// run函數(shù)
val people = People("carson", 25)
val result = people?.run {
  println("my name is $name, I am $age years old")
  999
}
// 最終結(jié)果 = 返回999 給變量result.

// apply函數(shù)
val people = People("carson", 25)
val result = people?.apply{
  println("my name is $name, I am $age years old")
  999
}
// 最終結(jié)果 = 返回一個(gè)people對(duì)象給變量result.

變量, 屬性

變量的聲明
//java
View v;

//kotlin
var v: View

kotlin中使用var關(guān)鍵字來聲明一個(gè)變量, 并且先寫變量名后寫它的類型, 中間用:分割.
但是如果這樣寫, IDE會(huì)報(bào)錯(cuò).

class Sample {
    var v: View
    //這樣寫IDE會(huì)報(bào)如下錯(cuò)誤.
    //Property must be initialized or be abstract
}

這是因?yàn)閗otlin的變量是沒有默認(rèn)值的, 而java的類成員變量是有默認(rèn)值的.
但如果簡(jiǎn)單的設(shè)置一個(gè)null值給kotlin變量也是不行的.

class Sample {
    var v: View = null
    //這樣寫IDE仍然會(huì)報(bào)錯(cuò).
    //Null can not be a value of a non-null type View
}

這就涉及到kotlin的空安全設(shè)計(jì).
kotlin的空安全設(shè)計(jì), 就是通過IDE來檢查代碼, 避免調(diào)用空對(duì)象的方法, 進(jìn)而引發(fā)NullPointerException.
在kotlin里面, 所有的變量默認(rèn)都是不準(zhǔn)許為空的.
如果要解除這個(gè)限制, 就需要在類型后面加一個(gè)?

class User {
    var name : String? = null
}

這種類型之后加?的寫法, 在kotlin里叫做可空類型.
但直接對(duì)這個(gè)變量用.調(diào)用, 就會(huì)報(bào)下面的錯(cuò)誤.

var view: View? = null
view.setBackgroundColor(Color.RED)
//這樣寫會(huì)報(bào)錯(cuò), Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type View?
view?.setBackgroundColor(Color.RED)

?.的作用是告訴編譯器, 在運(yùn)行時(shí)檢查這個(gè)對(duì)象, 如果為null就不調(diào)用了, 如果不為null才調(diào)用它的方法.
也可以使用 !!.的方式來調(diào)用.

view!!.setBackgroundColor(Color.RED)

意思是告訴編譯器, 我保證這里的view一定是非空的, 編譯器你不要幫我做檢查了, 有什么后果我自己來承擔(dān), 不要在編譯時(shí)報(bào)語法錯(cuò)誤.

但在大多數(shù)情況下, 給一個(gè)變量初始化為null沒有任何意思, 因?yàn)槲覀儾⒉粫?huì)去使用一個(gè)空對(duì)象, 也就是說我很確定我用的時(shí)候絕對(duì)不為空, 但第一時(shí)間我沒法給它賦值.
kotlin給我們提供了一個(gè)選項(xiàng): 延遲初始化.

lateinit var view: View

override fun onCreate(...) {
    ...
    view = findViewById(R.id.tvContent)
}

這個(gè)lateinit的意思是: 告訴編譯器我沒法第一時(shí)間就初始化, 但我肯定會(huì)在使用它之前完成初始化的. 它的作用就是讓IDE不要對(duì)這個(gè)變量檢查初始化和報(bào)錯(cuò). 換句話說, 加了這個(gè)lateinit關(guān)鍵字, 這個(gè)變量的初始化就全靠你自己了, 編譯器不幫你檢查了.

變量的類型推斷

如果你在聲明的時(shí)候就賦值, 那不寫變量類型也行.

var name: String = "Mike"
var name = "Mike"
val 和var

var是variable的縮寫, val是value的縮寫.
val 和 java 中的final類似.

val size = 10
屬性委托 by 和 by lazy
by lazy 的使用

簡(jiǎn)單說, 就是實(shí)現(xiàn)屬性的懶加載, 也就是說只有這個(gè)屬性被使用的時(shí)候才會(huì)計(jì)算這個(gè)屬性的值, 避免在初始化的時(shí)候就去計(jì)算屬性值, 這樣可以提高一些性能, 還有一個(gè)特性是對(duì)這個(gè)屬性的多次訪問操作, 只會(huì)在第一次訪問的時(shí)候去計(jì)算屬性的值. 后面再訪問這個(gè)屬性的時(shí)候, 就直接使用之前計(jì)算好的值就可以了, 這樣也可以提高一些性能.

// play.kotlinlang.org 是個(gè)好網(wǎng)站
val testValue: String by lazy {
    println("computed! ---- 只調(diào)用一次.")
    "Hello"
}

fun main() {
      val name = "world"
      println(name)

      println(testValue)
      println(testValue)
      println(testValue)

}

上次訪問了3次testValue的值, 但實(shí)際上, 只在第一次訪問的時(shí)候計(jì)算一次這個(gè)屬性的值.
輸出結(jié)果是:

world
computed! ---- 只調(diào)用一次.
Hello
Hello
Hello

說白了, 使用了by lazy關(guān)鍵字的變量, 對(duì)它值的計(jì)算就只算一次就可以了, 之后這個(gè)值就被緩存起來了, 不會(huì)再重復(fù)計(jì)算, 可以提高些代碼的運(yùn)行效率.

by 的使用

數(shù)組和集合

數(shù)組
val strs: Array<String> = arrayOf("a", "b", "c")
println(strs[0])
strs[1] = "B"

這里有個(gè)比較偏的概念, 叫做協(xié)變(covariance)特性. 其實(shí)指的就是子類數(shù)組對(duì)象不能賦值給父類的數(shù)組變量.

val strs: Array<String> = arrayOf("a", "b", "c")
val anys: Array<Any> = strs //compile-error: Type mismatch.

Any 就是和java中的Object概念一樣, 是任何類的基類.

集合

kotlin和java一樣有三種集合類型: List, Set 和 Map.

List
// java 中
List<String> strList = new ArrayList<>();
strList.add("a");
strList.add("b");
strList.add("c");
//kotlin
val strList = listOf("a", "b", "c")

而且 kotlin中的List多了一個(gè)特性: 支持covariant (協(xié)變). 也就是說, 可以把子類的List賦值給父類的List變量.

val strs: List<String> = listOf("a", "b", "c")
val anys: List<Any> = strs // success

而這在 Java 中是會(huì)報(bào)錯(cuò)的:

List<String> strList = new ArrayList<>();
List<Object> objList = strList; // compile error: incompatible types

在kotlin play中做了驗(yàn)證: kotlin中數(shù)組Array 是不支持協(xié)變的, 但集合類List是支持協(xié)變的.

Set
//java 中
Set<String> strSet = new HashSet<>();
strSet.add("a");
strSet.add("b");
strSet.add("c");
//kotlin中
val strSet = setOf("a", "b", "c")

和List 類似, Set 同樣具備 covariant (協(xié)變) 特性.

Map

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容