Reflection 科特林:代表怎么样;是否访问了get和setValue方法?
Reflection 科特林:代表怎么样;是否访问了get和setValue方法?,reflection,delegates,kotlin,Reflection,Delegates,Kotlin,我一直想知道(“by”-关键字)在后台是如何工作的。我知道,根据约定,委托(by的右侧)必须实现get和setValue(…)方法,但是编译器如何确保这一点,以及如何在运行时访问这些方法呢?我最初的想法是,显然委托人必须让我实现某种“SuperDelegate”接口,但情况似乎并非如此。所以剩下的唯一选项(我知道)是使用反射来访问这些方法,可能在语言本身的低层实现。我觉得这有点奇怪,因为根据我的理解,这是相当低效的。而且反射API甚至不是stdlib的一部分,这使得它更加奇怪 我假设后者已经是答
我一直想知道(“by”-关键字)在后台是如何工作的。
我知道,根据约定,委托(by的右侧)必须实现get和setValue(…)方法,但是编译器如何确保这一点,以及如何在运行时访问这些方法呢?
我最初的想法是,显然委托人必须让我实现某种“SuperDelegate”接口,但情况似乎并非如此。
所以剩下的唯一选项(我知道)是使用反射来访问这些方法,可能在语言本身的低层实现。我觉得这有点奇怪,因为根据我的理解,这是相当低效的。而且反射API甚至不是stdlib的一部分,这使得它更加奇怪 我假设后者已经是答案的一部分。因此,让我进一步问您以下问题:为什么没有SuperDelegate接口来声明我们被迫使用的getter和setter方法?那不是更干净吗?
以下不是问题的关键
所描述的接口甚至已经在和中定义。要决定使用哪一个,可以根据我们是否有val/var来确定。甚至可以忽略这一点,因为编译器阻止在val上调用setValue方法,并且只使用ReadWriteProperty接口作为超级委托
可以说,当要求委托实现某个接口时,构造的灵活性会降低。尽管这是假设用作委托的类可能不知道被用作委托,但考虑到对必要方法的特定要求,我发现这是不可能的。如果你仍然坚持,这里有一个疯狂的想法:为什么不让该类通过实现所需的接口(我知道目前还不可能,但见鬼,为什么不?可能有一个很好的“为什么不”,请让我知道作为旁注)。如果你看一下生成的Kotlin字节码,您将看到在包含您正在使用的委托的类中创建了一个私有字段,属性的
get
和set
方法只需调用该委托字段上的相应方法
由于委托的类在编译时是已知的,因此不必进行反射,只需简单的方法调用。委托约定(getValue
+setValue
)是在编译器端实现的,基本上没有一个解析逻辑在运行时执行:对委托对象的相应方法的调用直接放在生成的字节码中
让我们看一看为具有委托属性的类生成的字节码(您可以使用内置IntelliJ IDEA实现这一点):
我们可以在生成的字节码中找到以下内容:
- 这是类
的字段,用于存储对委托对象的引用:C
// access flags 0x12 private final Lkotlin/Lazy; x$delegate
- 这是构造函数(
)的一部分,初始化委托字段,将函数传递给
构造函数:惰性
ALOAD 0 GETSTATIC C$x$2.INSTANCE : LC$x$2; CHECKCAST kotlin/jvm/functions/Function0 INVOKESTATIC kotlin/LazyKt.lazy (Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy; PUTFIELD C.x$delegate : Lkotlin/Lazy;
- 这是
的代码: 您可以看到对getX()
的Lazy
方法的调用,该方法直接放在字节码中。事实上,编译器使用委托约定的正确签名解析该方法,并生成调用该方法的gettergetValue
迭代器
、比较
、调用
和其他可以重载的运算符——它们都是类似的,但它们的代码生成逻辑比委托的代码生成逻辑简单
但是,请注意,它们都不需要实现接口:可以为未实现可比
的类型定义compareTo
运算符,并且迭代器()
不要求该类型是可比
的实现,它们在编译时无论如何都会被解析
虽然接口方法可能比运算符惯例更简洁,但它允许的灵活性较低:例如,无法使用接口方法,因为它们无法编译为覆盖接口方法的方法。谢谢您的回答。大部分内容涵盖了我的问题。“没有这样的接口,因为它是不需要的(对于运行时性能)”-有道理。我仍然在想,为什么你不让代理实现这个接口呢。在我看来,这样更容易理解和创建代表。但我愿意把它放在那里:)(如果没有什么惊人的事情发生,我会接受一点)“扩展函数不允许方法重写”-让我明白了这一点。
ALOAD 0
GETSTATIC C$x$2.INSTANCE : LC$x$2;
CHECKCAST kotlin/jvm/functions/Function0
INVOKESTATIC kotlin/LazyKt.lazy (Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;
PUTFIELD C.x$delegate : Lkotlin/Lazy;
L0
ALOAD 0
GETFIELD C.x$delegate : Lkotlin/Lazy;
ASTORE 1
ALOAD 0
ASTORE 2
GETSTATIC C.$$delegatedProperties : [Lkotlin/reflect/KProperty;
ICONST_0
AALOAD
ASTORE 3
L1
ALOAD 1
INVOKEINTERFACE kotlin/Lazy.getValue ()Ljava/lang/Object;
L2
CHECKCAST java/lang/Number
INVOKEVIRTUAL java/lang/Number.intValue ()I
IRETURN