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)不同:
- Java中的public在kotlin中可以省略, kotlin的類默認(rèn)是public的.
- 類的繼承的寫法, Java里用的是extends, 而在kotlin里使用:, 但其實(shí) : 不僅可以表示繼承某個(gè)類, 還可以表示Java中的implement 某個(gè)接口.
- kotlin把構(gòu)造函數(shù)單獨(dú)用了一個(gè)constructor關(guān)鍵字來和其他的fun做區(qū)分.
- java里面@override是注解的形式, kotlin里的override變成了關(guān)鍵字.
- kotlin里的類默認(rèn)是final的, 也就是不能被其他類去繼承,要想讓這個(gè)類可以被繼承, 需要加上open關(guān)鍵字, 下面這樣的MainActivity就可以被繼承了.
- kotlin里創(chuàng)建類實(shí)例的時(shí)候, 不需要new關(guān)鍵字.
- 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)不同:
- java中的構(gòu)造器和類同名, kotlin中使用constructor關(guān)鍵字表示.
- 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):
- constructor構(gòu)造器移到了類名后面
- 類的成員變量name可以引用主構(gòu)造器中的參數(shù)name的值
- 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中有不少這樣的使用案例.

用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