一、類定義
Kotlin 類可以包含:構(gòu)造函數(shù)和初始化代碼塊、函數(shù)、屬性、內(nèi)部類、對(duì)象聲明。
Kotlin 中使用關(guān)鍵字 class 聲明類,后面緊跟類名:
//Kotlin 中的類可以有一個(gè) 主構(gòu)造器,以及一個(gè)或多個(gè)次構(gòu)造器,主構(gòu)造器是類頭部的一部分,位于類名稱之后:
class Person constructor(firstName: String){
var name: String = ……
var url: String = ……
//Kotlin 中類不能有字段。提供了 Backing Fields(后端變量) 機(jī)制,備用字段使用field關(guān)鍵字聲明,field 關(guān)鍵詞只能用于屬性的訪問器
var num: Int = 100
get() = field // 后端變量
set(value) {
if (value < 10) { // 如果傳入的值小于 10 返回該值
field = value
} else {
field = -1 // 如果傳入的值大于等于 10 返回 -1
}
}
//非空屬性必須在定義的時(shí)候初始化,kotlin提供了一種可以延遲初始化的方案,使用 lateinit 關(guān)鍵字描述屬性:
lateinit var subject: TestSubject
//次構(gòu)造函數(shù)
constructor(parent: Person) {
// 初始化...
}
constructor (name: String, age:Int) : this(name) {
// 初始化...
}
init {
//初始化代碼
}
//在類中定義成員函數(shù)
fun foo() {
print("Foo")
}
}
// 創(chuàng)建對(duì)象,Kotlin 中沒有 new 關(guān)鍵字
val person = Person()
//使用屬性,只要用名稱引用它即可; 使用 . 號(hào)來引用
person.name
person.url
我們也可以定義一個(gè)空類:
class Empty
1、類的屬性
屬性定義
類的屬性可以用關(guān)鍵字 var 聲明為可變的,
否則使用只讀關(guān)鍵字 val 聲明為不可變。
getter 和 setter
屬性聲明的完整語法:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
getter 和 setter 都是可選
如果屬性類型可以從初始化語句或者類的成員函數(shù)中推斷出來,那就可以省去類型,val不允許設(shè)置setter函數(shù),因?yàn)樗侵蛔x的。
2、主次構(gòu)造器
- 主構(gòu)造器中不能包含任何代碼,初始化代碼可以放在初始化代碼段中,初始化代碼段使用 init 關(guān)鍵字作為前綴;
- 如果構(gòu)造器有注解,或者有可見度修飾符,這時(shí)constructor關(guān)鍵字是必須的,注解和修飾符要放在它之前;
- 次構(gòu)造函數(shù):類也可以有二級(jí)構(gòu)造函數(shù),需要加前綴 constructor;
- 如果類有主構(gòu)造函數(shù),每個(gè)次構(gòu)造函數(shù)都要,或直接或間接通過另一個(gè)次構(gòu)造函數(shù)代理主構(gòu)造函數(shù)。在同一個(gè)類中代理另一個(gè)構(gòu)造函數(shù)使用 this 關(guān)鍵字;
- 如果一個(gè)非抽象類沒有聲明構(gòu)造函數(shù)(主構(gòu)造函數(shù)或次構(gòu)造函數(shù)),它會(huì)產(chǎn)生一個(gè)沒有參數(shù)的構(gòu)造函數(shù)。構(gòu)造函數(shù)是 public 。如果你不想你的類有公共的構(gòu)造函數(shù),你就得聲明一個(gè)空的主構(gòu)造函數(shù):
class Person constructor(firstName: String) {
init {
println("FirstName is $firstName")
}
//第3 類也可以有二級(jí)構(gòu)造函數(shù),需要加前綴 constructor:
constructor(parent: Person) {
parent.children.add(this)
}
//第4
constructor (name: String, age:Int) : this(name) {
// 初始化...
}
//第5
class DontCreateMe private constructor () {
}
}
主構(gòu)造函數(shù)-->init代碼塊-->次構(gòu)造函數(shù)
二、類的修飾符
類的修飾符包括 classModifier 和_accessModifier_:
classModifier: 類屬性修飾符,標(biāo)示類本身特性。
abstract // 抽象類
final // 類不可繼承,默認(rèn)屬性
enum // 枚舉類
open // 類可繼承,類默認(rèn)是final的
annotation // 注解類
accessModifier: 訪問權(quán)限修飾符
private // 僅在同一個(gè)文件中可見
protected // 同一個(gè)文件中或子類可見
public // 所有調(diào)用的地方都可見
internal // 同一個(gè)模塊中可見
三、抽象類
抽象是面向?qū)ο缶幊痰奶卣髦唬惐旧恚蝾愔械牟糠殖蓡T,都可以聲明為abstract的。抽象成員在類中不存在具體的實(shí)現(xiàn)。
注意:無需對(duì)抽象類或抽象成員標(biāo)注open注解。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
四、嵌套類
我們可以把類嵌套在其他類中,看以下實(shí)例:
class Outer { // 外部類
private val bar: Int = 1
class Nested { // 嵌套類
fun foo() = 2
}
}
fun main(args: Array<String>) {
val demo = Outer.Nested().foo() // 調(diào)用格式:外部類.嵌套類.嵌套類方法/屬性
println(demo) // == 2
}
kotlin 嵌套類相當(dāng)于 java 的靜態(tài)內(nèi)部類(有static );
kotlin 內(nèi)部類則對(duì)應(yīng)于 java 的非靜態(tài)內(nèi)部類(無 static)
五、內(nèi)部類
內(nèi)部類使用 inner 關(guān)鍵字來表示。
內(nèi)部類會(huì)帶有一個(gè)對(duì)外部類的對(duì)象的引用,所以內(nèi)部類可以訪問外部類成員屬性和成員函數(shù)。
class Outer {
private val bar: Int = 1
var v = "成員屬性"
/**嵌套內(nèi)部類**/
inner class Inner {
fun foo() = bar // 訪問外部類成員
fun innerTest() {
var o = this@Outer //獲取外部類的成員變量
println("內(nèi)部類可以引用外部類的成員,例如:" + o.v)
}
}
}
fun main(args: Array<String>) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 內(nèi)部類可以引用外部類的成員,例如:成員屬性
}
為了消除歧義,要訪問來自外部作用域的 this,我們使用this@label,其中 @label 是一個(gè) 代指 this 來源的標(biāo)簽。
六、匿名內(nèi)部類
kotlin 中:用對(duì)象表達(dá)式取代了匿名內(nèi)部類;
對(duì)象表達(dá)式用 object : 開頭,后面是要實(shí)現(xiàn)/繼承的父類/父接口。如果沒有父類/父接口,則只需要 object 即可:
var myobject = object {
init {
println("init called")
}
//實(shí)現(xiàn)方法
}
//如果它需要實(shí)現(xiàn)多個(gè)接口或父類,用逗號(hào)分隔
var myobject = object: MyInterface, MyAbstractClass() {
override fun print(i: Int) {
println("i = $i")
}
}
class Test {
var v = "成員屬性"
fun setInterFace(test: TestInterFace) {
test.test()
}
}
/**
* 定義接口
*/
interface TestInterFace {
fun test()
}
fun main(args: Array<String>) {
var test = Test()
/**
* 采用對(duì)象表達(dá)式來創(chuàng)建接口對(duì)象,即匿名內(nèi)部類的實(shí)例。
*/
test.setInterFace(object : TestInterFace {
override fun test() {
println("對(duì)象表達(dá)式創(chuàng)建匿名內(nèi)部類的實(shí)例")
}
})
}
對(duì)象表達(dá)式 vs lambda 表達(dá)式
在 kotlin 中,如果對(duì)象表達(dá)式實(shí)現(xiàn)的是一個(gè)函數(shù)式接口(該接口只有一個(gè)抽象方法),我們可以將它轉(zhuǎn)換成 lambda 表達(dá)式。
fun setInterFace(test: TestInterFace) {
test.test()
}
fun setInterFace{
test.test()
}
七、數(shù)據(jù)類
Kotlin 可以創(chuàng)建一個(gè)只包含數(shù)據(jù)的類,關(guān)鍵字為 data:
data class User(val name: String, val age: Int)
編譯器會(huì)自動(dòng)的從主構(gòu)造函數(shù)中根據(jù)所有聲明的屬性提取以下函數(shù):
equals() / hashCode()
toString() 格式如 "User(name=John, age=42)"
componentN() functions 對(duì)應(yīng)于屬性,按聲明順序排列
copy() 函數(shù)
如果這些函數(shù)在類中已經(jīng)被明確定義了,或者從超類中繼承而來,就不再會(huì)生成。
為了保證生成代碼的一致性以及有意義,數(shù)據(jù)類需要滿足以下條件:
- 主構(gòu)造函數(shù)至少包含一個(gè)參數(shù)。
- 所有的主構(gòu)造函數(shù)的參數(shù)必須標(biāo)識(shí)為val 或者 var ;
- 數(shù)據(jù)類不可以聲明為 abstract, open, sealed 或者 inner;
- 數(shù)據(jù)類不能繼承其他類 (但是可以實(shí)現(xiàn)接口)。
標(biāo)準(zhǔn)數(shù)據(jù)類
標(biāo)準(zhǔn)庫提供了 Pair 和 Triple 。在大多數(shù)情形中,命名數(shù)據(jù)類是更好的設(shè)計(jì)選擇,因?yàn)檫@樣代碼可讀性更強(qiáng)而且提供了有意義的名字和屬性。
八、密封類
密封類用來表示受限的類繼承結(jié)構(gòu):當(dāng)一個(gè)值為有限幾種的類型, 而不能有任何其他類型時(shí)。
在某種意義上,他們是枚舉類的擴(kuò)展:枚舉類型的值集合 也是受限的,但每個(gè)枚舉常量只存在一個(gè)實(shí)例,而密封類的一個(gè)子類可以有可包含狀態(tài)的多個(gè)實(shí)例。
聲明一個(gè)密封類,使用 sealed 修飾類,密封類可以有子類,但是所有的子類都必須要內(nèi)嵌在密封類中。
sealed 不能修飾 interface ,abstract class(會(huì)報(bào) warning,但是不會(huì)出現(xiàn)編譯錯(cuò)誤)
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
使用密封類的關(guān)鍵好處在于使用 when 表達(dá)式 的時(shí)候,如果能夠 驗(yàn)證語句覆蓋了所有情況,就不需要為該語句再添加一個(gè) else 子句了。
九、枚舉類
枚舉類最基本的用法是實(shí)現(xiàn)一個(gè)類型安全的枚舉。
枚舉常量用逗號(hào)分隔,每個(gè)枚舉常量都是一個(gè)對(duì)象。
enum class Color{
RED,BLACK,BLUE,GREEN,WHITE
}
枚舉還支持以聲明自己的匿名類及相應(yīng)的方法、以及覆蓋基類的方法。
enum class ProtocolState {
WAITING {
override fun signal() = TALKING
},
TALKING {
override fun signal() = WAITING
};
abstract fun signal(): ProtocolState
}
使用枚舉常量
Kotlin 中的枚舉類具有合成方法,允許遍歷定義的枚舉常量,并通過其名稱獲取枚舉常數(shù)。
EnumClass.valueOf(value: String): EnumClass // 轉(zhuǎn)換指定 name 為枚舉值,若未匹配成功,會(huì)拋出IllegalArgumentException
EnumClass.values(): Array<EnumClass> // 以數(shù)組的形式,返回枚舉值
獲取枚舉相關(guān)信息:
val name: String //獲取枚舉名稱
val ordinal: Int //獲取枚舉值在所有枚舉數(shù)組中定義的順序