Kotlin基礎(chǔ)-類和對(duì)象

一、類定義

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)造器
  1. 主構(gòu)造器中不能包含任何代碼,初始化代碼可以放在初始化代碼段中,初始化代碼段使用 init 關(guān)鍵字作為前綴;
  2. 如果構(gòu)造器有注解,或者有可見度修飾符,這時(shí)constructor關(guān)鍵字是必須的,注解和修飾符要放在它之前;
  3. 次構(gòu)造函數(shù):類也可以有二級(jí)構(gòu)造函數(shù),需要加前綴 constructor;
  4. 如果類有主構(gòu)造函數(shù),每個(gè)次構(gòu)造函數(shù)都要,或直接或間接通過另一個(gè)次構(gòu)造函數(shù)代理主構(gòu)造函數(shù)。在同一個(gè)類中代理另一個(gè)構(gòu)造函數(shù)使用 this 關(guān)鍵字;
  5. 如果一個(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ù)組中定義的順序
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,250評(píng)論 6 530
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 97,923評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,041評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,475評(píng)論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,253評(píng)論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,801評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,882評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,023評(píng)論 0 285
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,530評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,494評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,639評(píng)論 1 366
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,177評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,890評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,289評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,552評(píng)論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,242評(píng)論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,626評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容