我开始怀疑通过 ::test
访问属性是否等同于调用 { test }
或者它是否是使用反射的间接调用。
在查看以下内容时,我想到了这个问题:How can I pass property getter as a function type to another function
虽然 ::test
和 { test }
都可以工作,但 IDE (Intellij) 将 ::test
设置为 KProperty
-type 而后一种类型在分配给变量时是 () -> String
。所以这里有区别。但有效的区别是什么?这些是 Java 中的真实方法引用还是访问属性的反射方式?一个变体可能会对另一个变体产生任何性能影响吗?
代码片段:
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?
}
}
最佳答案
我认为在这种情况下最好检查字节码以了解发生了什么。
我使用了以下代码:
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
类从其父类 PropertyReference0
继承一个 invoke
方法,该方法调用其 get()
方法,而 >$1
立即声明 invoke
。因此,与 $1
相比,如果没有进一步优化,$2
会执行一个额外的方法调用。
关于kotlin - 当作为参数传递时,属性引用 (::test) 是否等效于访问属性 ({ test }) 的函数,例如 `() -> String` ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51842043/