当作为参数传递时,属性引用(:: test)是否等效于访问属性({test})的函数,例如`() - > String`?

Rol*_*and 8 kotlin

我开始想知道访问属性::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)

use*_*612 7

我认为在这种情况下,最好检查字节码以查看发生了什么.

我使用了以下代码:

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.