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
修饰
- 会被转换成
静态成员
- 在
kotlin
和Java
中访问时,都可以以静态成员
的方式访问
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
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/8031809.html