我开始想知道访问属性::test是否等同于调用{ test }或者它是否是使用反射的间接调用.
在查看以下内容时,我想到了一个问题:如何将属性getter作为函数类型传递给另一个函数
同时兼具::test和{ test }工作时,IDE(的Intellij)设置::test到一个KProperty型而后者类型是() -> 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?
}
}
Run Code Online (Sandbox Code Playgroud)
我认为在这种情况下,最好检查字节码以查看发生了什么.
我使用了以下代码:
class Test(val test: String) {
fun testFun(func: () -> String): Unit = TODO()
fun callTest() {
testFun { test }
testFun(::test)
}
}
Run Code Online (Sandbox Code Playgroud)
对于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
Run Code Online (Sandbox Code Playgroud)
这是字节码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
Run Code Online (Sandbox Code Playgroud)
它们看起来几乎完全相同,只是第一个是创建一个Test$callTest$1,而第二个是使用a 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;
}
}
Run Code Online (Sandbox Code Playgroud)
同时$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
}
}
Run Code Online (Sandbox Code Playgroud)
所以看起来在字节码指令方面没有太大的区别.
编辑:类从其父类$2继承一个invoke方法PropertyReference0,该get()方法调用其方法,同时$1立即声明invoke.因此,没有进一步的优化,$2执行一个额外的方法调用$1.
| 归档时间: |
|
| 查看次数: |
56 次 |
| 最近记录: |