我开始怀疑是通过::test
访问属性是否等效于调用{ test }
还是使用反射进行的间接调用。
在查看以下内容时,我想到了这个问题:How can I pass property getter as a function type to another function
虽然::test
和{ test }
都可以工作,但是IDE(Intellij)将::test
设置为KProperty
类型,而当() -> String
类型为class Test(val test : String) {
fun testFun(func: ()->String) : String = func()
fun callTest() {
testFun { test } // or (::test) // is it using reflection? are these real references?
}
}
时分配给变量。所以这里有区别。但是有效的区别是什么?这些真正的方法引用是Java中的引用,还是它们作为访问属性的一种反映方式?一种变体可能会对另一种变体产生性能影响吗?
代码段:
.cta-43274891247129739-info {
position: absolute;
left: 0;
top: 50px;
margin: 10px 10px;
animation: cta-43274891247129739 4s 1s both ease-out;
text-align: center;
}
@keyframes cta-43274891247129739 {
0% {
transform: translateY(1em);
opacity: 0;
}
16.6667%, 83.3333% {
opacity: 1;
transform: translateY(0em);
}
100% {
transform: translateY(-40px);
}
}
答案 0 :(得分:7)
我认为在这种情况下,最好检查字节码以查看发生了什么。
我使用了以下代码:
class Test(val test: String) {
fun testFun(func: () -> String): Unit = TODO()
fun callTest() {
testFun { test }
testFun(::test)
}
}
对于testFun { test }
,这是生成的字节码:
ALOAD 0
NEW Test$callTest$1
DUP
ALOAD 0
INVOKESPECIAL Test$callTest$1.<init> (LTest;)V
CHECKCAST kotlin/jvm/functions/Function0
INVOKEVIRTUAL Test.testFun (Lkotlin/jvm/functions/Function0;)V
这是testFun(::test)
的字节码:
ALOAD 0
NEW Test$callTest$2
DUP
ALOAD 0
CHECKCAST Test
INVOKESPECIAL Test$callTest$2.<init> (LTest;)V
CHECKCAST kotlin/jvm/functions/Function0
INVOKEVIRTUAL Test.testFun (Lkotlin/jvm/functions/Function0;)V
它们看上去几乎完全相同,除了第一个正在创建Test$callTest$1
,而第二个正在使用Test$callTest$2
。第二个“版本”中还有一个额外的CHECKCAST Test
,因为Test$callTest$2
在其构造函数中期望Test
的实例。
那么$1
和$2
有什么区别?
这里是$1
版:
final class Test$callTest$1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function0 {
....
final synthetic LTest; this$0 // reference to your Test instance
<init>(LTest;)V {
ALOAD 0
ALOAD 1
PUTFIELD Test$callTest$1.this$0 : LTest;
ALOAD 0
ICONST_0 // Lambda arity is zero
INVOKESPECIAL kotlin/jvm/internal/Lambda.<init> (I)V
RETURN
}
public final String invoke() {
ALOAD 0
GETFIELD Test$callTest$1.this$0 : LTest;
INVOKEVIRTUAL Test.getTest ()Ljava/lang/String;
}
}
而$2
:
final class Test$callTest$2 extends kotlin/jvm/internal/PropertyReference0 {
...
<init>(LTest;)V {
ALOAD 0
ALOAD 1
INVOKESPECIAL kotlin/jvm/internal/PropertyReference0.<init> (Ljava/lang/Object;)V
RETURN
}
public Object get() {
GETFIELD Test$callTest$2.receiver : Ljava/lang/Object;
CHECKCAST Test
INVOKEVIRTUAL Test.getTest ()Ljava/lang/String;
ARETURN
}
}
所以看来字节码指令并没有太大区别。
编辑:
类$2
继承自其父类invoke
的{{1}}方法,该方法调用其PropertyReference0
方法,而get()
立即声明$1
。因此,与invoke
相比,$2
无需执行进一步的优化。