所以我在 kotlin 中有一个简单的类和一个从该类获取字段的函数:
class SomeClass {
var value = 1
}
fun showValue() {
val klass = SomeClass()
println(klass.value)
}
函数字节码如下所示:
public final static showValue()V
L0
LINENUMBER 8 L0
NEW com/korol/kittybot/SomeClass
DUP
INVOKESPECIAL com/korol/kittybot/SomeClass.<init> ()V
ASTORE 0
L1
LINENUMBER 9 L1
ALOAD 0
INVOKEVIRTUAL com/korol/kittybot/SomeClass.getValue ()I
ISTORE 1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ILOAD 1
INVOKEVIRTUAL java/io/PrintStream.println (I)V
L2
LINENUMBER 10 L2
RETURN
L3
LOCALVARIABLE klass Lcom/korol/kittybot/SomeClass; L1 L3 0
MAXSTACK = 2
MAXLOCALS = 2
由于某种原因,getter 不是静态的。
INVOKEVIRTUAL com/korol/kittybot/SomeClass.getValue
。我很感兴趣为什么。据我了解,虚拟调用适用于具有多种实现的函数。但这里的领域不是开放的,类既不是抽象的,也不是开放的。如果有人能解释一下,我将不胜感激。
Kotlin 通常不会在类中生成静态。它只能在同伴或物体中生成。
首先我要说的是,我不是 Kotlin 程序员。
由于某种原因,getter 不是静态的。
getter 不是静态的,因为您获取的属性不是静态的。 它是(用 Java 术语来说)
final
,但是 static
和 final
是不同的东西。
(方法)的区别是:
对于您的 getter 示例:
SomeClass
的 value
实例,因此 getter cannot 是 static
(在 Java 意义上)。final
(在 Java 意义上),因为 SomeClass
不是 open
(在 Kotlin 意义上)。那么 JVM 字节码呢?
INVOKESTATIC
用于调用 static
方法。 它不允许有目标对象。INVOKEVIRTUAL
用于调用常规非静态方法。 它期望操作栈上的目标对象。INVOKEINTERFACE
用于调用接口而不是子类层次结构中定义的非静态方法。INVOKESPECIAL
适用于特殊情况:构造函数、类初始化,但可以用于调用 final
方法,前提是该方法可以在编译时绑定。INVOKEDYNAMIC
适用于需要更灵活(运行时)调度方法调用的(其他)特殊情况。在您的示例中,Kotlin 编译器可以发出
INVOKEVIRTUAL
或 INVOKESPECIAL
字节码来调用 getter。 两者都会导致调用正确的 getter 方法。 唯一的区别是(理论上)性能。
在实践中,现代 HotSpot 优化器可以(在运行时)确定方法实际上是最终方法,并优化硬件调用序列以消除通过虚拟方法表进行的任何不必要的调度。 因此,无论发出 INVOKEVIRTUAL
还是 INVOKESPECIAL
,
1
都没有任何区别。
1 - 假设包含调用的代码运行频率足以触发优化。