伴生对象调用的原理
屏幕快照 2020-10-23 下午12.55.26.png伴生对象在编译后会生成一个静态内部类
加了@JvmStatic注解,才是真正的静态成员
在Kotlin扩展函数的解析是不支持多态的,它只看声明的类型,而不看实际的类型
屏幕快照 2020-10-23 下午3.04.08.png我们如果只从中读取数据,而不往里面写入内容,那么这样的对象叫做生产者,也就是协变,此时使用 ? extends E;如果只向里面写入数据,而不从中读取数据,那么这样的对象叫做消费者,也就是逆变,使用? super E。
in只会保证写,而不能读,而out则相反
委托实现的原理
interface IWash {
fun wash()
}
class BigHeadSon : IWash {
override fun wash() {
println("我是小头儿子,我洗碗才1块钱")
}
}
class SmallHeadFather : IWash by BigHeadSon()
@Metadata(
mv = {1, 1, 16},
bv = {1, 0, 3},
k = 1,
d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\t\u0010\u0003\u001a\u00020\u0004H\u0096\u0001¨\u0006\u0005"},
d2 = {"Lcom/example/kotlinapp/delegate/SmallHeadFather;", "Lcom/example/kotlinapp/delegate/IWash;", "()V", "wash", "", "app"}
)
public final class SmallHeadFather implements IWash {
// $FF: synthetic field
private final BigHeadSon $$delegate_0 = new BigHeadSon();
public void wash() {
this.$$delegate_0.wash();
}
}
从原理分析Kotlin的延迟初始化 lateinit var 和 by lazy
public final class LateinitTest {
private String name;
public final void kk() {
this.name = "hello";
}
}
public final class LazyTest {
private final Lazy age$delegate;
private final int getAge() {
Lazy var1 = this.age$delegate;
Object var3 = null;
boolean var4 = false;
return ((Number)var1.getValue()).intValue();
}
public final void printAge() {
int var1 = this.getAge();
boolean var2 = false;
System.out.println(var1);
}
public LazyTest() {
this.age$delegate = LazyKt.lazy((Function0)null.INSTANCE);
}
}
by lazy 具体 怎么实现呢?
1,生成一个该属性的附加属性,例如:age$delegate
2,在构造器中,给 age$delegate赋值
3,而name$$delegate.getVaule()方法的返回结果是对象name$$delegate内部的_value属性值,在getVaule()第一次被调用时会将_value进行初始化,往后都是直接将_value的值返回,从而实现属性值的唯一一次初始化。
注意,对value的初始化行为本身是线程安全的。
lateinit var 和 by lazy的区别
-
by lazy要求属性声明为val,即不可变变量,在java中相当于被final修饰。
-
by lazy可以使用于类属性或者局部变量。
-
lateinit var只能用来修饰类属性
使用Kotlin Reified让泛型更加简单安全的原理
通过inline函数保证使得泛型类的类型实参在运行时能够保留,这样的操作Kotlin中把它称为实化,对应需要使用reified关键字。
关于inline函数补充一点: 编译器把实现内联函数的字节码动态插入到每次的调用点,它能使泛型函数类型实参进行实化,在运行时能拿到类型实参的信息。
总之一句话很简单,就是带实化参数的函数每次调用都生成不同类型实参的字节码,动态插入到调用点。由于生成的字节码的类型实参引用了具体的类型,而不是类型参数所以不会存在擦除问题。
reified关键字只能标记实化类型参数的内联函数,不能作用与类和属性。
inline fun <reified T> isInstanceOf(value: Any): Boolean = value is T
//定义了一个实化类型参数T,并且它有类型形参上界约束Activity,它可以直接将实化类型参数T当做普通类型使用
inline fun <reified T: Activity> Context.startActivity(vararg params: Pair<String, Any?>) =
AnkoInternals.internalStartActivity(this, T::class.java, params)
扩展函数实现 原理分析
fun String.hello(world : String) : String {
return "hello " + world + this.length;
}
@Metadata(
mv = {1, 1, 16},
bv = {1, 0, 3},
k = 2,
d1 = {"\u0000\n\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0002\u001a\u0012\u0010\u0000\u001a\u00020\u0001*\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0001¨\u0006\u0003"},
d2 = {"hello", "", "world", "app"}
)
public final class HelloExtFunctionKt {
@NotNull
public static final String hello(@NotNull String $this$hello, @NotNull String world) {
Intrinsics.checkParameterIsNotNull($this$hello, "$this$hello");
Intrinsics.checkParameterIsNotNull(world, "world");
return "hello " + world + $this$hello.length();
}
}
网友评论