你是否也被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语言的推广工做。

编程,咱们是认真的!

关注欧阳锋工做室公众号,你想要的都在这里:

欧阳锋工做室
相关文章
相关标签/搜索