你是否也被Kotlin語言的object繞暈了呢

文 | 歐陽鋒

近日,在筆者的Kotlin語言交流羣中。的確發現了一些同窗對object的用法有一些疑問。因而,出現了下面這樣錯誤的用法: express

很天然的想法,c是一個接口類型的成員變量,訪問外部類的成員變量,這不是理所應當的嗎?編程

即便查看Kotlin官方文檔,也有這樣一段描述:bash

Sometimes we need to create an object of a slight modification of some class, without explicitly declaring a new subclass for it. Java handles this case with anonymous inner classes. Kotlin slightly generalizes this concept with object expressions and object declarations.ide

核心意思是:Kotlin使用object代替Java匿名內部類實現。ui

很明顯,即使如此,這裏的訪問應該也是合情合理的。從匿名內部類中訪問成員變量在Java語言中是徹底容許的。this

這個問題頗有意思,解答這個咱們須要生成Java字節碼,再反編譯成Java看看具體生成的代碼是什麼。spa

藉助JD-GUI,咱們能夠看到下面的內容:code

public final class Outer
{
  private String a;

  public static final class c
    implements Moveable
  {
    public static final c INSTANCE;
    
    static
    {
      c localc = new c();INSTANCE = localc;
    }
    
    public void move()
    {
      Moveable.DefaultImpls.move(this);
    }
  }
}
複製代碼

頗有意思,咱們在Kotlin類中object部分的代碼最終變成了下面這個樣子:cdn

public static final class c implements Moveable {
    public static final c INSTANCE;
    static {
      c localc = new c();INSTANCE = localc;
    }
    
    public void move() {
      Moveable.DefaultImpls.move(this);
    }
  }
複製代碼

這是一個靜態內部類,很明顯,靜態內部類是不能訪問外部類成員變量的。但是問題來了,說好的匿名內部類呢?對象

這裏必定要注意,若是你只是這樣聲明瞭一個object,Kotlin認爲你是須要一個靜態內部類。而若是你用一個變量去接收object表達式,Kotlin認爲你須要一個匿名內部類對象。

所以,這個類應該這樣改進:

class Outer {
    private var a: String? = null
    
    // 用變量c去接收object表達式
    private val c = object: Moveable {
        override fun move() {
            super.move()
            // 改進後,這裏訪問正常
            println(a)
        }
    }
}
複製代碼

爲了不出現這個問題,謹記一個原則:若是object只是聲明,它表明一個靜態內部類。若是用變量接收object表達式,它表明一個匿名內部類對象。

object能幹啥?

很天然地想到,Kotlin的object到底有什麼做用。其實,從上文的表述來看。很明顯,object至少有下面兩個做用:

  • 簡化生成靜態內部類
  • 生成匿名內部類對象

其實,object還有一個很是重要的做用,就是生成單例對象。若是你須要在Kotlin語言中使用單例,很是簡單,只須要使用object關鍵字便可。

object Singleton {
    fun f1() {
        
    }
    
    fun f2() {
        
    }
}
複製代碼

這種方式聲明object和上面的方式略有區別,其最終會生成一個名爲Singleton的類,並在類中生成一個靜態代碼塊進行單例對象生成:

public final class Singleton
{
  public static final Singleton INSTANCE;
  
  public final void f1() {}
  
  public final void f2() {}
  
  static
  {
    Singleton localSingleton = new Singleton();INSTANCE = localSingleton;
  }
}
複製代碼

在Kotlin語言中對方法進行訪問的時候最終實際上是經過INSTANCE實例進行中轉的。

在Kotlin語言中還有一個很經常使用的object叫作伴隨對象。所謂的伴隨對象只不過是名字叫作Companion的object而已。它主要用於類中生成相似Java的靜態變量,Kotlin語言針對這個變量會認爲你只是但願生成一個靜態變量,而不但願引入多餘的類。若是你是和Java語言混合開發的話,可使用一個註解生成和Java語言靜態變量徹底同樣的效果。

簡單總結

Kotlin語言中使用object命名的方式的確容易讓人誤認爲只要使用這個關鍵字就是生成了一個對象。而從上文的表述當中,你會發現,其實不一樣的使用姿式將產生不一樣的效果。所以,在平常使用中必定要學會隨機應變。若是遇到了不明白的問題,不妨來看看這篇文章是否已經解答了你的問題。若是沒有,請在文章下方留言告訴我。

歡迎加入Kotlin交流羣

若是你也喜歡Kotlin語言,歡迎加入個人Kotlin交流羣: 329673958 ,一塊兒來參與Kotlin語言的推廣工做。

編程,咱們是認真的!

關注歐陽鋒工做室公衆號,你想要的都在這裏:

歐陽鋒工做室
相關文章
相關標籤/搜索