通过java中互相调用和编译后的字节码或反编译来分析。本质上是能够相互转换代码的。
1. 对象申明:修饰class名
object className {} 的类名申明方式。
object ObjectClass {
var a = 0
fun foo(){}
}
//反编译得到如下
/* loaded from: ObjectClass.class */
public final class ObjectClass {
public static final ObjectClass INSTANCE = new ObjectClass();
private static int a;
private ObjectClass() {
}
public final int getA() {
return a;
}
public final void setA(int i) {
a = i;
}
public final void foo() {
}
}
因此,object class
就是java中,final的class配上private构造函数,加恶汉单例INSTANCE
。
如果我们要在java中调用,还得通过MyCalss.INSTANCE.a()
来调用。
备注:想让a()函数变成static,则通过注解@JVMStatic
注解来实现,对象变量通过@JVMField
。
object ObjectClass {
@JvmField
var a = 0
@JvmStatic
fun foo(){}
}
//反编译成如下:
public final class ObjectClass {
@NotNull
public static final ObjectClass INSTANCE = new ObjectClass();
@JvmField
public static int a;
private ObjectClass() {
}
@JvmStatic
public static final void foo() {
}
}
2. 伴生对象:companion object
class MyClass {
companion object {
val aa = 1
fun a(){}
}
}
//反编译为java代码如下:
public final class MyClass {
private static final int aa = 1;
public static final Companion Companion = new Companion((DefaultConstructorMarker)null);
public static final class Companion {
public final int getAa() {
return MyClass.aa;
}
public final void a() {
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
在Kotlin中调用MyClass.a()
, 在Java中调用 MyClass.Companion.a();
理解第一章的object class后,再理解伴生对象事半功倍。变量是放在主类中的static变量。而函数则是伴生类(静态内部类)中的函数。同样的,觉得这种调用不爽,可以通过注解@JVMStatic或者@JVMField处理。
3. 对象表达式
3.1 基本用法
Thread(object :Runnable{
override fun run() {
println("hah")
}
}).start()
Collections.sort(list, object : Comparator<String?> {
override fun compare(o1: String?, o2: String?): Int {
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
return o1.compareTo(o2)
}
})
//lambda写法
Collections.sort(list, { o1: String, o2: String ->
if (o1 == null) {
return@sort -1;
}
if (o2 == null) {
return@sort 1;
}
return@sort o1.compareTo(o2)
})
匿名内部类,也会有IDEA提示,建议改成lambda(备注2点,第一点,有的可以提示,有的不提示。第二点,与java不同,java里面箭头后面用花括号包住即可,而kotlin不能添加{})。kotlin的lambda省略写法很多,这里不做介绍,按照idea提示自行转换吧。
3.2 更强大的用法
多个继承使用,达到了超越java的匿名内部类的能力。
val superMan = object: Man("Clark"), Flyable, SuperHearing, SuperVision {
override fun hearSubtleNoises() {
}
override fun seeThroughWalls() {
}
override fun fly() {
}
}
3.3 对象表达式-直用-函数
//Object a = clas.publicFoo();
//外部只能获取到一个object。没有任何用处。
//推荐如下加上private。
fun publicFoo() = object {
val x: String = "y"
fun getValue() = 6
}
//私有object表达式
private fun foo() = object {
val x: String = "y"
fun getValue() = 6
}
//测试上面的代码
fun test() {
val f = foo()
val xx = f.x
f.getValue()
}
1.5 对象表达式-直用-变量
//无法被引用内部参数,是个any
val publicValue = object {
val x: String = "y"
fun getValue() = 6
}
//可以被引用
private val privateValue = object {
val x = "aaa"
fun getValue() = 7
}
private fun test() {
privateValue.x
val v = privateValue.getValue()
}
内存泄漏研究
仔细阅读下图。是通过仔细分析字节码研究出来的,有错误之处,尽情回复拍砖。
kotlin唯一的好处是,对于没有引用外部类实例的情况下,在对象表达式上有所优化。
kotlin分为:
静态内部类,内部类,匿名内部类,lambda表达式。
第一种:java内部不用static修饰,持有外部引用,对应kotlin 添加inner标记;
第二种:java内部使用static修饰,不持有外部引用,对应直接申明class的kotlin内部类;
public class Test {
int x = 1;
//《1. 内部类》:会持有外部引用。
class Inner{
void a() {
x = x + 1;
}
}
//《2. 静态内部类》不持有外部引用
static class Inner{
void a() {
x = x + 1;
}
}
void foo() {
//new Runnable属于《3. 匿名内部类》
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
}
void foo() {
//《4. lambda》
new Thread(() -> {
}).start();
}
class MyClass {
var aaa = "a"
//持有外部引用。
inner class NestClass {
fun a(): String {
return aaa
}
}
//不持有外部引用,相当于static final class
class NestClass {
fun a(): String {
return aaa
}
}
}