Kotlin知識(shí)歸納(四) —— 接口和類

????Kotlin的類和接口與Java的類和接口存在較大區(qū)別冗懦,本次主要?dú)w納Kotlin的接口和類如何定義猾漫、繼承以及其一些具體細(xì)節(jié)鲁纠,同時(shí)查看其對(duì)應(yīng)的Java層實(shí)現(xiàn)妖枚。

帶默認(rèn)方法的接口

????Kotlin接口可以包含抽象方法以及非抽象方法的實(shí)現(xiàn)(類似Java 8的默認(rèn)方法)

interface MyInterface {
    //抽象方法
    fun daqi()
    //非抽象方法(即提供默認(rèn)實(shí)現(xiàn)方法)
    fun defaultMethod() {
    }
}

????接口也可以定義屬性魄衅。聲明的屬性可以是抽象的峭竣,也可以是提供具體訪問器實(shí)現(xiàn)的(即不算抽象的)。

interface MyInterface {
    //抽象屬性
    var length:Int
    //提供訪問器的屬性
    val name:String
        get() = ""

    //抽象方法
    fun daqi()
    //非抽象方法(即提供默認(rèn)實(shí)現(xiàn)方法)
    fun defaultMethod() {
    }
}

????接口中聲明的屬性不能有幕后字段晃虫。因?yàn)榻涌谑菬o(wú)狀態(tài)的皆撩,因此接口中聲明的訪問器不能引用它們。(簡(jiǎn)單說就是接口沒有具體的屬性,不能用幕后字段對(duì)屬性進(jìn)行賦值)

image

接口的實(shí)現(xiàn)

????Kotlin使用 : 替代Java中的extends 和 implements 關(guān)鍵字扛吞。Kotlin和Java一樣呻惕,一個(gè)類可以實(shí)現(xiàn)任意多個(gè)接口,但是只能繼承一個(gè)類滥比。

????接口中抽象的方法和抽象屬性亚脆,實(shí)現(xiàn)接口的類必須對(duì)其提供具體的實(shí)現(xiàn)。

????對(duì)于在接口中提供默認(rèn)實(shí)現(xiàn)的接口方法和提供具體訪問器的屬性盲泛,可以對(duì)其進(jìn)行覆蓋濒持,重新實(shí)現(xiàn)方法和提供新的訪問器實(shí)現(xiàn)。

class MyClass:MyInterface{
    //原抽象屬性寺滚,提供具體訪問器
    //不提供具體訪問器柑营,提供初始值,使用默認(rèn)訪問器也是沒有問題的
    override var length: Int = 0
    /*override var length: Int
        get() = 0
        set(value) {}*/
    
    //覆蓋提供好訪問器的接口屬性
    override val name: String
        //super.name 其實(shí)是調(diào)用接口中定義的訪問器
        get() = super.name
    
    //原抽象方法玛迄,提供具體實(shí)現(xiàn)
    override fun daqi() {
    }

    //覆蓋默認(rèn)方法
    override fun defaultMethod() {
        super.defaultMethod()
    }
}

????無(wú)論是從接口中獲取的屬性還是方法由境,前面都帶有一個(gè)override關(guān)鍵字。該關(guān)鍵字與Java的@Override注解類似蓖议,重寫父類或接口的方法屬性時(shí)虏杰,都 強(qiáng)制 需要用override修飾符進(jìn)行修飾。因?yàn)檫@樣可以避免先寫出實(shí)現(xiàn)方法勒虾,再添加抽象方法造成的意外重寫纺阔。

接口的繼承

????接口也可以從其他接口中派生出來,從而既提供基類成員的實(shí)現(xiàn)修然,也可以聲明新的方法和屬性笛钝。

interface Name {
    val name:String
}

interface Person :Name{
    fun learn()
}

class daqi:Person{
    //為父接口的屬性提供具體的訪問器
    override val name: String
        get() = "daqi"
    
    //為子接口的方法提供具體的實(shí)現(xiàn)
    override fun learn() {
    }
}

覆蓋沖突

????在C++中,存在菱形繼承的問題愕宋,即一個(gè)類同時(shí)繼承具有相同函數(shù)簽名的兩個(gè)方法玻靡,到底該選擇哪一個(gè)實(shí)現(xiàn)呢?由于Kotlin的接口支持默認(rèn)方法中贝,當(dāng)一個(gè)類實(shí)現(xiàn)多個(gè)接口囤捻,同時(shí)擁有兩個(gè)具有相同函數(shù)簽名的默認(rèn)方法時(shí),到底選擇哪一個(gè)實(shí)現(xiàn)呢邻寿?

主要根據(jù)以下3條規(guī)則進(jìn)行判斷:

????1蝎土、類中帶override修飾的方法優(yōu)先級(jí)最高。 類或者父類中帶override修飾的方法的優(yōu)先級(jí)高于任何聲明為默認(rèn)方法的優(yōu)先級(jí)绣否。(Kotlin編譯器強(qiáng)制要求誊涯,當(dāng)類中存在和父類或?qū)崿F(xiàn)的接口有相同函數(shù)簽名的方法存在時(shí),需要在前面添加override關(guān)鍵字修飾蒜撮。)

????2暴构、當(dāng)?shù)谝粭l無(wú)法判斷時(shí),子接口的優(yōu)先級(jí)更高。優(yōu)先選擇擁有最具體實(shí)現(xiàn)的默認(rèn)方法的接口丹壕,因?yàn)閺睦^承角度理解庆械,可以認(rèn)為子接口的默認(rèn)方法覆蓋重寫了父接口的默認(rèn)方法,子接口比父接口具體菌赖。

????3缭乘、最后還是無(wú)法判斷時(shí),繼承多個(gè)接口的類需要顯示覆蓋重寫該方法琉用,并選擇調(diào)用期望的默認(rèn)方法堕绩。

  • 如何理解第二條規(guī)則?先看看一下例子:

Java繼承自Language邑时,兩者都對(duì)use方法提供了默認(rèn)實(shí)現(xiàn)奴紧。而Java比Language更具體。

interface Language{
    fun use() = println("使用語(yǔ)言")
}

interface Java:Language{
    override fun use() = println("使用Java語(yǔ)言編程")
}

而實(shí)現(xiàn)這兩個(gè)接口的類中晶丘,并無(wú)覆蓋重寫該方法黍氮,只能選擇更具體的默認(rèn)方法作為其方法實(shí)現(xiàn)。

class Person:Java,Language{
}

//執(zhí)行結(jié)果是輸出:使用Java語(yǔ)言編程
val daqi = Person()
daqi.use()
  • 如何理解第三條規(guī)則浅浮?繼續(xù)看例子:

接口Java和Kotlin都提供對(duì)learn方法提供了具體的默認(rèn)實(shí)現(xiàn)沫浆,且兩者并無(wú)明確的繼承關(guān)系。

interface Java {
    fun learn() = println("學(xué)習(xí)Java")
}

interface Kotlin{
    fun learn() = println("學(xué)習(xí)Kotlin")
}

當(dāng)某類都實(shí)現(xiàn)Java和Kotlin接口時(shí)滚秩,此時(shí)就會(huì)產(chǎn)生覆蓋沖突的問題专执,這個(gè)時(shí)候編譯器會(huì)強(qiáng)制要求你提供自己的實(shí)現(xiàn):

image

唯一的解決辦法就是顯示覆蓋該方法,如果想沿用接口的默認(rèn)實(shí)現(xiàn)郁油,可以super關(guān)鍵字本股,并將具體的接口名放在super的尖括號(hào)中進(jìn)行調(diào)用。

class Person:Java,Kotlin{
    override fun learn() {
        super<Java>.learn()
        super<Kotlin>.learn()
    }
}

對(duì)比Java 8的接口

????Java 8中也一樣可以為接口提供默認(rèn)實(shí)現(xiàn)桐腌,但需要使用default關(guān)鍵字進(jìn)行標(biāo)識(shí)拄显。(Kotlin只需要提供具體的方法實(shí)現(xiàn),即提供函數(shù)體)

public interface Java8 {
    default void defaultMethod() {
        System.out.println("我是Java8的默認(rèn)方法"); 
    }
} 

????面對(duì)覆蓋沖突案站,Java8的和處理和Kotlin的基本相似凿叠,在語(yǔ)法上顯示調(diào)用接口的默認(rèn)方法時(shí)有些不同:

//Java8 顯示調(diào)用覆蓋沖突的方法
Java8.super.defaultMethod()
    
//Kotlin 顯示調(diào)用覆蓋沖突的方法
super<Kotlin>.learn()

Kotlin 與 Java 間接口的交互

????眾所周知,Java8之前接口沒有默認(rèn)方法嚼吞,Kotlin是如何兼容的呢?定義如下兩個(gè)接口蹬碧,再查看看一下反編譯的結(jié)果:

interface Language{
    //默認(rèn)方法
    fun use() = println("使用語(yǔ)言編程")
}


interface Java:Language{
    //抽象屬性
    var className:String

    //提供訪問器的屬性
    val field:String
        get() = ""

    //默認(rèn)方法
    override fun use() = println("使用Java語(yǔ)言編程")

    //抽象方法
    fun absMethod()
}

先查看父接口的源碼:

public interface Language {
   void use();

   public static final class DefaultImpls {
      public static void use(Language $this) {
         String var1 = "使用語(yǔ)言編程";
         System.out.println(var1);
      }
   }
}

????Language接口中的默認(rèn)方法轉(zhuǎn)換為抽象方法保留在接口中舱禽。其內(nèi)部定義了一個(gè)名為DefaultImpls的靜態(tài)內(nèi)部類,該內(nèi)部類中擁有和默認(rèn)方法相同名稱的靜態(tài)方法恩沽,而該靜態(tài)方法的實(shí)現(xiàn)就是其同名默認(rèn)函數(shù)的具體實(shí)現(xiàn)誊稚。也就是說,Kotlin的默認(rèn)方法轉(zhuǎn)換為靜態(tài)內(nèi)部類DefaultImpls的同名靜態(tài)函數(shù)。

所以里伯,如果想在Java中調(diào)用Kotlin接口的默認(rèn)方法城瞎,需要加多一層DefaultImpls

public class daqiJava implements Language {
    @Override
    public void use() {
        Language.DefaultImpls.use(this);
    }
}

再繼續(xù)查看子接口的源碼

public interface Java extends Language {
   //抽象屬性的訪問器
   @NotNull 
   String getClassName();
   void setClassName(@NotNull String var1);

   //提供具體訪問器的屬性
   @NotNull 
   String getField();

    //默認(rèn)方法
   void use();
    
    //抽象方法
   void absMethod();
    
   public static final class DefaultImpls {
      @NotNull
      public static String getField(Java $this) {
         return "";
      }

      public static void use(Java $this) {
         String var1 = "使用Java語(yǔ)言編程";
         System.out.println(var1);
      }
   }
}

????通過源碼觀察到,無(wú)論是抽象屬性還是擁有具體訪問器的屬性疾瓮,都沒有在接口中定義任何屬性脖镀,只是聲明了對(duì)應(yīng)的訪問器方法。(和擴(kuò)展屬性相似)

抽象屬性和提供具體訪問器的屬性區(qū)別是:

  • 抽象屬性的訪問器均為抽象方法狼电。
  • 擁有具體訪問器的屬性至耻,其訪問器實(shí)現(xiàn)和默認(rèn)方法一樣羽圃,外部聲明一個(gè)同名抽象方法,具體實(shí)現(xiàn)被存儲(chǔ)在靜態(tài)內(nèi)部類DefaultImpls的同名靜態(tài)函數(shù)中。

Java定義的接口,Kotlin繼承后能為其父接口的方法提供默認(rèn)實(shí)現(xiàn)嗎惫皱?當(dāng)然是可以啦:

//Java接口
public interface daqiInterface {
    String name = "";
    
    void absMethod();
}

//Kotlin接口
interface daqi: daqiInterface {
    override fun absMethod() {

    }
}

????Java接口中定義的屬性都是默認(rèn)public static final,對(duì)于Java的靜態(tài)屬性擎淤,在Kotlin中可以像頂層屬性一樣肮砾,直接對(duì)其進(jìn)行使用:

fun main(args: Array<String>) {
    println("Java接口中的靜態(tài)屬性name = $name")
}

????Kotlin的類可以有一個(gè)主構(gòu)造函數(shù)以及一個(gè)或多個(gè) 從構(gòu)造函數(shù)。主構(gòu)造函數(shù)是類頭的一部分髓抑,即在類體外部聲明咙崎。

主構(gòu)造方法

constructor關(guān)鍵字可以用來聲明 主構(gòu)造方法 或 從構(gòu)造方法。

class Person(val name:String)
//其等價(jià)于
class Person constructor(val name:String)

????主構(gòu)造函數(shù)不能包含任何的代碼启昧。初始化的代碼可以放到以 init 關(guān)鍵字作為前綴的初始化塊中叙凡。

class Person constructor(val name:String){
    init {
        println("name = $name")
    }
}

????構(gòu)造方法的參數(shù)也可以設(shè)置為默認(rèn)參數(shù),當(dāng)所有構(gòu)造方法的參數(shù)都是默認(rèn)參數(shù)時(shí)密末,編譯器會(huì)生成一個(gè)額外的不帶參數(shù)的構(gòu)造方法來使用所有的默認(rèn)值握爷。

class Person constructor(val name:String = "daqi"){
    init {
        println("name = $name")
    }
}

//輸出為:name = daqi
fun main(args: Array<String>) {
    Person()
}

????主構(gòu)造方法同時(shí)需要初始化父類,子類可以在其列表參數(shù)中索取父類構(gòu)造方法所需的參數(shù)严里,以便為父類構(gòu)造方法提供參數(shù)新啼。

open class Person constructor(name:String){
}

class daqi(name:String):Person(name){
}

????當(dāng)沒有給一個(gè)類聲明任何構(gòu)造方法,編譯器將生成一個(gè)不做任何事情的默認(rèn)構(gòu)造方法刹碾。對(duì)于只有默認(rèn)構(gòu)造方法的類燥撞,其子類必須顯式地調(diào)用父類的默認(rèn)構(gòu)造方法,即使他沒有參數(shù)迷帜。

open class View
    
class Button:View()

而接口沒有構(gòu)造方法物舒,所以接口名后不加括號(hào)。

//實(shí)現(xiàn)接口
class Button:ClickListener

當(dāng) 主構(gòu)造方法 有注解或可見性修飾符時(shí)戏锹,constructor 關(guān)鍵字不可忽略冠胯,并且constructor 在這些修飾符和注解的后面。

class Person public @Inject constructor(val name:String)

構(gòu)造方法的可見性是 public锦针,如果想將構(gòu)造方法設(shè)置為私有荠察,可以使用private修飾符置蜀。

class Person private constructor()

從構(gòu)造方法

從構(gòu)造方法使用constructor關(guān)鍵字進(jìn)行聲明

open class View{
    //從構(gòu)造方法1
    constructor(context:Context){
    }
    
    //從構(gòu)造方法2
    constructor(context:Context,attr:AttributeSet){
    }
}

????使用this關(guān)鍵字,從一個(gè)構(gòu)造方法中調(diào)用該類另一個(gè)構(gòu)造方法悉盆,同時(shí)也能使用super()關(guān)鍵字調(diào)用父類構(gòu)造方法盯荤。

????如果一個(gè)類有 主構(gòu)造方法,每個(gè) 從構(gòu)造方法 都應(yīng)該顯式調(diào)用 主構(gòu)造方法焕盟,否則將其委派給會(huì)調(diào)用主構(gòu)造方法的從構(gòu)造方法秋秤。

class Person constructor(){
    //從構(gòu)造方法1,顯式調(diào)用主構(gòu)造方法
    constructor(string: String) : this() {
        println("從構(gòu)造方法1")
    }
    
    //從構(gòu)造方法2京髓,顯式調(diào)用構(gòu)造方法1航缀,間接調(diào)用主構(gòu)造方法。
    constructor(data: Int) : this("daqi") {
        println("從構(gòu)造方法2")
    }
}

注意

????初始化塊中的代碼實(shí)際上會(huì)成為主構(gòu)造函數(shù)的一部分堰怨。顯式調(diào)用主構(gòu)造方法會(huì)作為次構(gòu)造函數(shù)的第一條語(yǔ)句芥玉,因此所有初始化塊中的代碼都會(huì)在次構(gòu)造函數(shù)體之前執(zhí)行。

即使該類沒有主構(gòu)造函數(shù)备图,這種調(diào)用仍會(huì)隱式發(fā)生灿巧,并且仍會(huì)執(zhí)行初始化塊。

//沒有主構(gòu)造方法的類
class Person{
    init {
        println("主構(gòu)造方法 init 1")
    }
    
    //從構(gòu)造方法默認(rèn)會(huì)執(zhí)行所有初始化塊
    constructor(string: String) {
        println("從構(gòu)造方法1")
    }

    init {
        println("主構(gòu)造方法 init 2")
    }
}
image

????如果一個(gè)類擁有父類揽涮,但沒有主構(gòu)造方法時(shí)抠藕,每個(gè)從構(gòu)造方法都應(yīng)該初始化父類(即調(diào)用父類的構(gòu)造方法),否則將其委托給會(huì)初始化父類的構(gòu)造方法(即使用this調(diào)用其他會(huì)初始化父類的構(gòu)造方法)蒋困。

class MyButton:View{
    //調(diào)用自身的另外一個(gè)從構(gòu)造方法盾似,間接調(diào)用父類的構(gòu)造方法。
    constructor(context:Context):this(context,MY_STYLE){
    }
    //調(diào)用父類的構(gòu)造方法雪标,初始化父類零院。
    constructor(context:Context,attr:AttributeSet):super(context,attr){
    }
}

脆弱的基類

????Java中允許創(chuàng)建任意類的子類并重寫任意方法,除非顯式地使用final關(guān)鍵字村刨。對(duì)基類進(jìn)行修改導(dǎo)致子類不正確的行為告抄,就是所謂的脆弱的基類。所以Kotlin中類和方法默認(rèn)是final嵌牺,Java類和方法默認(rèn)是open的打洼。

????當(dāng)你允許一個(gè)類存在子類時(shí),需要使用open修飾符修改這個(gè)類逆粹。如果想一個(gè)方法能被子類重寫募疮,也需要使用open修飾符修飾。

open class Person{
    //該方法時(shí)final 子類不能對(duì)它進(jìn)行重寫
    fun getName(){}
    
    //子類可以對(duì)其進(jìn)行重寫
    open fun getAge(){}
}

對(duì)基類或接口的成員進(jìn)行重寫后僻弹,重寫的成員同樣默認(rèn)為open酝锅。(盡管其為override修飾)

如果想改變重寫成員默認(rèn)為open的行為,可以顯式的將重寫成員標(biāo)注為final

open class daqi:Person(){
    final override fun getAge() {
        super.getAge()
    }
}

抽象類的成員和接口的成員始終是open的奢方,不需要顯式地使用open修飾符搔扁。

image

可見性修飾符

????Kotlin和Java的可見性修飾符相似,同樣可以使用public蟋字、protected和private修飾符稿蹲。但Kotlin默認(rèn)可見性是public,而Java默認(rèn)可見性是包私有鹊奖。

????Kotlin中并沒有包私有這種可見性苛聘,Kotlin提供了一個(gè)新的修飾符:internal,表示“只在模塊內(nèi)部可見”。模塊是指一組一起編譯的Kotlin文件忠聚∩杌可能是一個(gè)Gradle項(xiàng)目,可能是一個(gè)Idea模塊两蟀。internal可見性的優(yōu)勢(shì)在于它提供了對(duì)模塊實(shí)現(xiàn)細(xì)節(jié)的封裝网梢。

????Kotlin允許在頂層聲明中使用private修飾符,其中包括類聲明赂毯,方法聲明和屬性聲明战虏,但這些聲明只能在聲明它們的文件中可見。

注意

  • 覆蓋一個(gè) protected 成員并且沒有顯式指定其可見性党涕,該成員的可見性還是 protected 烦感。
  • 與Java不同,Kotlin的外部類(嵌套類)不能看到其內(nèi)部類中的private成員膛堤。
  • internal修飾符編譯成字節(jié)碼轉(zhuǎn)Java后手趣,會(huì)變成public。
  • private類轉(zhuǎn)換為Java時(shí),會(huì)變成包私有聲明肥荔,因?yàn)镴ava中類不能聲明為private绿渣。

內(nèi)部類和嵌套類

????Kotlin像Java一樣,允許在一個(gè)類中聲明另一個(gè)類次企。但Kotlin的嵌套類默認(rèn)不能訪問外部類的實(shí)例怯晕,和Java的靜態(tài)內(nèi)部類一樣。

????如果想讓Kotlin內(nèi)部類像Java內(nèi)部類一樣缸棵,持有一個(gè)外部類的引用的話舟茶,需要使用inner修飾符。

image

內(nèi)部類需要外部類引用時(shí)堵第,需要使用 this@外部類名 來獲取吧凉。

class Person{
    private val name  = "daqi"
    
    inner class MyInner{
        fun getPersonInfo(){
            println("name = ${this@Person.name}")
        }
    }
}

object關(guān)鍵字

對(duì)象聲明

????在Java中創(chuàng)建單例往往需要定義一個(gè)private的構(gòu)造方法,并創(chuàng)建一個(gè)靜態(tài)屬性來持有這個(gè)類的單例踏志。

????Kotlin通過對(duì)象聲明將類聲明和類的單一實(shí)例結(jié)合在一起阀捅。對(duì)象聲明在定義的時(shí)候就立即創(chuàng)建,而這個(gè)初始化過程是線程安全的针余。

????對(duì)象聲明中可以包含屬性饲鄙、方法凄诞、初始化語(yǔ)句等,也支持繼承類和實(shí)現(xiàn)接口忍级,唯一不允許的是不能定義構(gòu)造方法(包括主構(gòu)造方法和從構(gòu)造方法)帆谍。

????對(duì)象聲明不能定義在方法和內(nèi)部類中,但可以定義在其他的對(duì)象聲明和非內(nèi)部類(例如:嵌套類)轴咱。如果需要引用該對(duì)象汛蝙,直接使用其名稱即可。

//定義對(duì)象聲明
class Book private constructor(val name:String){

    object Factory {
        val name = "印書廠"

        fun createAppleBooK():Book{
            return Book("Apple")
        }

        fun createAndroidBooK():Book{
            return Book("Android")
        }
    }
}

調(diào)用對(duì)象聲明的屬性和方法:

Book.Factory.name
Book.Factory.createAndroidBooK()

????將對(duì)象聲明反編譯成Java代碼朴肺,其內(nèi)部實(shí)現(xiàn)也是定義一個(gè)private的構(gòu)造方法窖剑,并始終創(chuàng)建一個(gè)名為INSTANCE的靜態(tài)屬性來持有這個(gè)類的單例,而該類的初始化放在靜態(tài)代碼塊中戈稿。

public final class Book {
   //....

   public Book(String name, DefaultConstructorMarker $constructor_marker) {
      this(name);
   }

   public static final class Factory {
      @NotNull
      private static final String name = "印書廠";
      public static final Book.Factory INSTANCE;

      //...

      @NotNull
      public final Book createAppleBooK() {
         return new Book("Apple", (DefaultConstructorMarker)null);
      }

      @NotNull
      public final Book createAndroidBooK() {
         return new Book("Android", (DefaultConstructorMarker)null);
      }

      private Factory() {
      }

      static {
         Book.Factory var0 = new Book.Factory();
         INSTANCE = var0;
         name = "印書廠";
      }
   }
}

用Java調(diào)用對(duì)象聲明的方法:

//Java調(diào)用對(duì)象聲明
Book.Factory.INSTANCE.createAndroidBooK();

伴生對(duì)象

????一般情況下西土,使用頂層函數(shù)可以很好的替代Java中的靜態(tài)函數(shù),但頂層函數(shù)無(wú)法訪問類的private成員器瘪。

????當(dāng)需要定義一個(gè)方法翠储,該方法能在沒有類實(shí)例的情況下,調(diào)用該類的內(nèi)部方法橡疼≡可以定義一個(gè)該類的對(duì)象聲明,并在該對(duì)象聲明中定義該方法欣除。類內(nèi)部的對(duì)象聲明可以用 companion 關(guān)鍵字標(biāo)記住拭,這種對(duì)象叫伴生對(duì)象。

????可以直接通過類名來訪問該伴生對(duì)象的方法和屬性历帚,不用再顯式的指明對(duì)象聲明的名稱滔岳,再訪問該對(duì)象聲明對(duì)象的方法和屬性⊥炖危可以像調(diào)用該類的靜態(tài)函數(shù)和屬性一樣谱煤,不需要再關(guān)心對(duì)象聲明的名稱。

//將構(gòu)造方法私有化
class Book private constructor(val name:String){
    //伴生對(duì)象的名稱可定義也可以不定義禽拔。
    companion object {
        //伴生對(duì)象調(diào)用其內(nèi)部私有構(gòu)造方法
        fun createAppleBooK():Book{
            return Book("Apple")
        }

        fun createAndroidBooK():Book{
            return Book("Android")
        }
    }
}

調(diào)用伴生對(duì)象的方法:

Book.createAndroidBooK()

????伴生對(duì)象的實(shí)現(xiàn)和對(duì)象聲明類似刘离,定義一個(gè)private的構(gòu)造方法,并始終創(chuàng)建一個(gè)名為Companion的靜態(tài)屬性來持有這個(gè)類的單例睹栖,并直接對(duì)Companion靜態(tài)屬性進(jìn)行初始化硫惕。

public final class Book {
   //..
   public static final Book.Companion Companion = new Book.Companion((DefaultConstructorMarker)null);

    //...

   public static final class Companion {
     //...
      @NotNull
      public final Book createAppleBooK() {
         return new Book("Apple", (DefaultConstructorMarker)null);
      }

      @NotNull
      public final Book createAndroidBooK() {
         return new Book("Android", (DefaultConstructorMarker)null);
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

伴生對(duì)象的擴(kuò)展

????擴(kuò)展方法機(jī)制允許在任何地方定義某類的擴(kuò)展方法,但需要該類的實(shí)例進(jìn)行調(diào)用野来。當(dāng)需要擴(kuò)展一個(gè)通過類自身調(diào)用的方法時(shí)恼除,如果該類擁有伴生對(duì)象,可以通過對(duì)伴生對(duì)象定義擴(kuò)展方法曼氛。

//對(duì)伴生對(duì)象定義擴(kuò)展方法
fun Book.Companion.sellBooks(){
}

當(dāng)對(duì)該擴(kuò)展方法進(jìn)行調(diào)用時(shí)豁辉,可以直接通過類自身進(jìn)行調(diào)用:

Book.sellBooks()

匿名內(nèi)部類

作為android開發(fā)者令野,在設(shè)置監(jiān)聽時(shí),創(chuàng)建匿名對(duì)象的情況再常見不過了秋忙。

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        
    }
});

????object關(guān)鍵字除了能用來聲明單例式對(duì)象外彩掐,還可以聲明匿名對(duì)象。和對(duì)象聲明不同灰追,匿名對(duì)象不是單例,每次都會(huì)創(chuàng)建一個(gè)新的對(duì)象實(shí)例狗超。

mRecyclerView.setOnClickListener(object :View.OnClickListener{
    override fun onClick(v: View?) {
        
    }
});

????當(dāng)該匿名類擁有兩個(gè)以上抽象方法時(shí)弹澎,才需要使用object創(chuàng)建匿名類。否則盡量使用lambda表達(dá)式努咐。

mButton.setOnClickListener {
}

參考文獻(xiàn):

android Kotlin系列:

Kotlin知識(shí)歸納(一) —— 基礎(chǔ)語(yǔ)法

Kotlin知識(shí)歸納(二) —— 讓函數(shù)更好調(diào)用

Kotlin知識(shí)歸納(三) —— 頂層成員與擴(kuò)展

Kotlin知識(shí)歸納(四) —— 接口和類

Kotlin知識(shí)歸納(五) —— Lambda

Kotlin知識(shí)歸納(六) —— 類型系統(tǒng)

Kotlin知識(shí)歸納(七) —— 集合

Kotlin知識(shí)歸納(八) —— 序列

Kotlin知識(shí)歸納(九) —— 約定

Kotlin知識(shí)歸納(十) —— 委托

Kotlin知識(shí)歸納(十一) —— 高階函數(shù)

Kotlin知識(shí)歸納(十二) —— 泛型

Kotlin知識(shí)歸納(十三) —— 注解

Kotlin知識(shí)歸納(十四) —— 反射

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末苦蒿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子渗稍,更是在濱河造成了極大的恐慌佩迟,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竿屹,死亡現(xiàn)場(chǎng)離奇詭異报强,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拱燃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門秉溉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碗誉,你說我怎么就攤上這事召嘶。” “怎么了哮缺?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵弄跌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我尝苇,道長(zhǎng)铛只,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任茎匠,我火速辦了婚禮格仲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诵冒。我一直安慰自己凯肋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布汽馋。 她就那樣靜靜地躺著侮东,像睡著了一般圈盔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悄雅,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天驱敲,我揣著相機(jī)與錄音,去河邊找鬼宽闲。 笑死众眨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的容诬。 我是一名探鬼主播娩梨,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼览徒!你這毒婦竟也來了狈定?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤习蓬,失蹤者是張志新(化名)和其女友劉穎纽什,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躲叼,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芦缰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了押赊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饺藤。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖流礁,靈堂內(nèi)的尸體忽然破棺而出涕俗,到底是詐尸還是另有隱情,我是刑警寧澤神帅,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布再姑,位于F島的核電站,受9級(jí)特大地震影響找御,放射性物質(zhì)發(fā)生泄漏元镀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一霎桅、第九天 我趴在偏房一處隱蔽的房頂上張望栖疑。 院中可真熱鬧,春花似錦滔驶、人聲如沸遇革。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)萝快。三九已至锻霎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間揪漩,已是汗流浹背旋恼。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奄容,地道東北人冰更。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像昂勒,于是被迫代替她去往敵國(guó)和親冬殃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345