一停撞、類定義
Kotlin 類可以包含:構(gòu)造函數(shù)和初始化代碼塊县踢、函數(shù)转绷、屬性、內(nèi)部類硼啤、對象聲明议经。
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)建對象籍救,Kotlin 中沒有 new 關(guān)鍵字
val person = Person()
//使用屬性,只要用名稱引用它即可渠抹; 使用 . 號來引用
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ù):類也可以有二級構(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ù)),它會產(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 類也可以有二級構(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)。
注意:無需對抽象類或抽象成員標(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)部類則對應(yīng)于 java 的非靜態(tài)內(nèi)部類(無 static)
五啄踊、內(nèi)部類
內(nèi)部類使用 inner 關(guān)鍵字來表示寸潦。
內(nèi)部類會帶有一個(gè)對外部類的對象的引用,所以內(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 中:用對象表達(dá)式取代了匿名內(nèi)部類;
對象表達(dá)式用 object : 開頭狐血,后面是要實(shí)現(xiàn)/繼承的父類/父接口。如果沒有父類/父接口易核,則只需要 object 即可:
var myobject = object {
init {
println("init called")
}
//實(shí)現(xiàn)方法
}
//如果它需要實(shí)現(xiàn)多個(gè)接口或父類匈织,用逗號分隔
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()
/**
* 采用對象表達(dá)式來創(chuàng)建接口對象,即匿名內(nèi)部類的實(shí)例牡直。
*/
test.setInterFace(object : TestInterFace {
override fun test() {
println("對象表達(dá)式創(chuàng)建匿名內(nèi)部類的實(shí)例")
}
})
}
對象表達(dá)式 vs lambda 表達(dá)式
在 kotlin 中缀匕,如果對象表達(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)
編譯器會自動的從主構(gòu)造函數(shù)中根據(jù)所有聲明的屬性提取以下函數(shù):
equals() / hashCode()
toString() 格式如 "User(name=John, age=42)"
componentN() functions 對應(yīng)于屬性饵史,按聲明順序排列
copy() 函數(shù)
如果這些函數(shù)在類中已經(jīng)被明確定義了满钟,或者從超類中繼承而來,就不再會生成胳喷。
為了保證生成代碼的一致性以及有意義湃番,數(shù)據(jù)類需要滿足以下條件:
- 主構(gòu)造函數(shù)至少包含一個(gè)參數(shù)。
- 所有的主構(gòu)造函數(shù)的參數(shù)必須標(biāo)識為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(會報(bào) warning,但是不會出現(xiàn)編譯錯誤)
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è)類型安全的枚舉。
枚舉常量用逗號分隔,每個(gè)枚舉常量都是一個(gè)對象。
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 為枚舉值,若未匹配成功徘键,會拋出IllegalArgumentException
EnumClass.values(): Array<EnumClass> // 以數(shù)組的形式练对,返回枚舉值
獲取枚舉相關(guān)信息:
val name: String //獲取枚舉名稱
val ordinal: Int //獲取枚舉值在所有枚舉數(shù)組中定義的順序