End

Kotlin 朱涛-5 object 匿名内部类 单例 JvmStatic

本文地址


目录

匿名内部类

使用 object 创建匿名内部类

Kotlin 中可以使用 object 关键字创建匿名内部类:

// 定义匿名内部类的同时还创建了一个对象
image.setOnClickListener(object: View.OnClickListener {
    override fun onClick(v: View) {
        funA()
    }
})

// 可以使用 Lambda 简化为:
image.setOnClickListener { funA() }

可在定义的同时实现多个接口

Kotlin 中的匿名内部类,可以在定义的同时,实现多个接口

interface A { fun funA():String }
interface B { fun funB():String }
abstract class Man { abstract fun funC():String }

fun main() {
    val item = object : Man(), A, B { // 在定义匿名内部类的同时,实现多个接口
        override fun funA() = "funA"
        override fun funB() = "funB"
        override fun funC() = "funC"
    }
    println(item.funA() + item.funB() + item.funC())
}

底层实现是普通命名内部类

将上述代码反编译成 Java 代码,可以看到如下代码片段:

<undefinedtype> item = new A() { // 语法错误,因为找不到这个类
   public String funA() {}
   public String funB() {}
   public String funC() {}
};

由于反编译过程会损失一些细节信息,所以上面的语法是错误的。

如果查看 Kotlin 编译后的字节码,可以发现,其底层实现其实就是一个普通命名内部类

public final class MainKt$main$item$1 extends Man implements A B {...} // 这是一个普通的内部类

由此也可以看出,这种通过反编译成 Java 代码,来分析 Kotlin 语法原理的方式,是有局限性的,因为并非所有的字节码都可以反编译成 Java 源码。

单例模式

特点:

  • 虚拟机保证线程安全及只初始化一次
  • 不支持懒加载
  • 不支持传参构造单例

① object 单例

使用 object 定义的单例,其中的成员

  • kotlin 中访问时,就像是类的静态成员一样
  • Java 中访问时,仍然需要通过单例的实例去访问 --- 所以,实际上并不是静态成员
object Inner {
    var name = "bqt"
    fun plus(a: Int, b: Int) = a + b
}

反编译后的 Java 代码

public final class Inner {
   private static String name;                       // 注意这里是 private 的,所以不能直接被访问

   public final int plus(int a,int b) { return a+b;} // 并不是静态方法
   public final String getName() { return name; }    // 成员属性实际上被转换成了 getter/setter 方法
   public final void setName(String v) { name = v; } // 这两个方法也不是静态方法

   public static final Inner INSTANCE;               // 静态代码块的单例模式
   private Inner() {}
   static { INSTANCE = new Inner(); name = "bqt"; }  // 初始化成员变量
}

kotlin 中访问

fun main() {
    println(Inner.name)       // 使用起来就像是【静态属性】一样
    println(Inner.plus(1, 2)) // 使用起来就像是【静态方法】一样
}

Java 中访问

上述 kotlin 代码反编译后的 Java 代码也和下面的代码一样

public static void main(String[] args) {
    System.out.println(Inner.INSTANCE.getName());  // 只能通过单例【INSTANCE】访问其 getter/setter 方法
    System.out.println(Inner.INSTANCE.plus(1, 2)); // 只能通过单例【INSTANCE】访问其普通方法
}

可以发现,使用 object 定义的单例,其中的成员,在 kotlin 中访问时,就像是类的静态成员一样。但是在 Java 中访问时,仍然需要通过单例的实例去访问。所以,但是实际上并不是静态成员。

② object + JvmStatic

使用 object 定义的单例,如果其中的成员使用 @JvmField@JvmStatic 修饰

  • 会被转换成静态成员
  • kotlinJava 中访问时,都可以以静态成员的方式访问
object Inner {
    @JvmField var name = "bqt"                   // 即可以使用 @JvmField  修饰属性 -- 静态属性
    @JvmStatic var tag = "it"                    // 也可以使用 @JvmStatic 修饰属性 -- 静态方法
    @JvmStatic fun plus(a: Int, b: Int) = a + b  // 但只能使用 @JvmStatic 修饰方法 -- 静态方法
}

反编译后的 Java 代码

public final class Inner {
   public static String name;                             // 使用 @JvmField  修饰的属性会变成静态属性
   public static final int plus(int a,int b){return a+b;} // 使用 @JvmStatic 修饰的方法会变成静态方法

   private static String tag;                             // 使用 @JvmStatic 修饰的属性【不会】变成静态属性
   public static final String getTag() { return tag;}     // 而是会把其 get/set 方法变成【静态方法】
   public static final void setTag(String t) { tag = t; } 

   public static final Inner INSTANCE;
   private Inner() {}
   static { INSTANCE = new Inner(); name = "bqt"; tag = "it"; }
}

kotlin 中访问

kotlin 中访问时,和添加 @JvmField@JvmStatic 之前是完全一样的

fun main() {
    println(Inner.name)       // 使用起来就像是【静态属性】一样
    println(Inner.tag)        // 使用起来就像是【静态属性】一样
    println(Inner.plus(1, 2)) // 使用起来就像是【静态方法】一样
}

Java 中访问

Java 中访问时,和添加 @JvmField@JvmStatic 之前不一样

public static void main(String[] args) {
    System.out.println(Inner.name);       // 使用起来就像是【静态属性】一样
    System.out.println(Inner.getTag());   // 使用起来就像是【静态方法】一样
    System.out.println(Inner.plus(1, 2)); // 使用起来就像是【静态方法】一样
}

可以发现:使用 object 定义的单例,如果其中的成员使用 @JvmField@JvmStatic 修饰,不管是在 kotlin 中还是在 Java 中,都可以以静态成员的方式访问。

实际上,他们所修饰的成员就是被转换成了静态成员

③ 嵌套的 object 单例

结论:和上面使用 object 定义的单例,没什么本质的区别。

使用 object 定义单例有一种特殊情况,就是把单例定义在一个类的内部,即声明一个嵌套的静态内部类单例

class Outer {
    object Inner {
        var name = "bqt"
        fun plus(a: Int, b: Int): Int = a + b
    }
}

反编译后的 Java 代码

public final class Outer {
   public static final class Inner {                  // 嵌套的静态内部类单例
      public final int plus(int a,int b){return a+b;} // 没什么特别
      private static String name;                     // 没什么特别
      public final String getName() { return name; }  // 没什么特别
      public final void setName(String v) {name = v;} // 没什么特别

      public static final Outer.Inner INSTANCE;       // 没什么特别
      private Inner() {}
      static { INSTANCE = new Outer.Inner(); name = "bqt";}
   }
}

访问时的代码

kotlin 中访问:

fun main() {
    println(Outer.Inner.name)       // 使用起来就像是 Inner 中的一个【静态属性】一样
    println(Outer.Inner.plus(1, 2)) // 使用起来就像是 Inner 中的一个【静态方法】一样
}

Java 中访问:

public static void main(String[] args) {
    System.out.println(Outer.Inner.INSTANCE.getName());  // 只能通过单例【INSTANCE】访问其 getter 方法
    System.out.println(Outer.Inner.INSTANCE.plus(1, 2)); // 只能通过单例【INSTANCE】访问其普通方法
}

可以发现,和上面使用 object 定义的单例,没什么本质的区别。

④ 嵌套的 object 单例 + JvmStatic

结论:和上面使用 object 定义的单例中使用 @JvmStatic@JvmField,没什么本质的区别。

class Outer {
   object Inner {
       @JvmField var name = "bqt"                   // 使用 @JvmField  修饰属性
       @JvmStatic var tag = "it"                    // 使用 @JvmStatic 修饰属性
       @JvmStatic fun plus(a: Int, b: Int) = a + b  // 使用 @JvmStatic 修饰方法
   }
}

反编译后的 Java 代码

public final class Outer {
   public static final class Inner {                         // 嵌套的静态内部类单例
      public static String name;                             // 没什么特别
      public static final int plus(int a,int b){return a+b;} // 没什么特别

      private static String tag;                             // 没什么特别
      public static final String getTag() { return tag;}     // 没什么特别
      public static final void setTag(String t) { tag = t; } // 没什么特别

      public static final Outer.Inner INSTANCE;              // 没什么特别
      private Inner() {}
      static { INSTANCE = new Outer.Inner(); name = "bqt"; tag = "it"; }
   }
}

访问时的代码

kotlin 中访问时的代码和之前完全一样。

Java 中访问时的代码:

public static void main(String[] args) {
    System.out.println(Outer.Inner.name);       // 使用起来就像是【静态属性】一样
    System.out.println(Outer.Inner.getTag());   // 使用起来就像是【静态方法】一样
    System.out.println(Outer.Inner.plus(1, 2)); // 使用起来就像是【静态方法】一样
}

可以发现,和上面使用 object 定义的单例中使用 @JvmStatic@JvmField,没什么本质的区别。

2017-12-13

posted @ 2017-12-13 11:00  白乾涛  阅读(1483)  评论(0编辑  收藏  举报