为什么隐式值 类 有一个额外的方法调用?
Why do implicit value classes have an extra method invocation?
我正在检查由隐式 类 生成的字节码,并想与它们扩展 AnyVal
.
时生成的内容进行比较
没有隐式:
object Example1 {
class Wrapper(val self: Int) extends AnyVal {
def add(n: Int): Int = self + n
}
def foo(w: Wrapper): Wrapper = new Wrapper(w.add(42))
}
字节码(的相关部分):
scala>:javap Example1
[...]
public int foo(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
0: getstatic #19 // Field Example1$Wrapper$.MODULE$:LExample1$Wrapper$;
3: iload_1
4: bipush 42
6: invokevirtual #23 // Method Example1$Wrapper$.add$extension:(II)I
9: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LExample1$;
0 10 1 w I
LineNumberTable:
line 11: 3
[...]
与implicit
:
object Example2 {
implicit class Wrapper(val self: Int) extends AnyVal {
def add(n: Int): Int = self + n
}
def foo(w: Wrapper): Wrapper = w.add(42)
}
字节码(的相关部分):
scala>:javap Example2
[...]
public int Wrapper(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=2
0: iload_1
1: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LExample2$;
0 2 1 self I
LineNumberTable:
line 9: 0
public int foo(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
0: aload_0
1: getstatic #23 // Field Example2$Wrapper$.MODULE$:LExample2$Wrapper$;
4: iload_1
5: bipush 42
7: invokevirtual #27 // Method Example2$Wrapper$.add$extension:(II)I
10: invokevirtual #29 // Method Wrapper:(I)I
13: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 this LExample2$;
0 14 1 w I
LineNumberTable:
line 12: 0
[...]
作为扩展 AnyVal
的结果,对伴随对象调用了对 add
的调用,并且类型 Wrapper
未显示在 [=20= 的类型签名中] (public int foo(int);
) 在两个版本中。
然而,在第二个版本中,return之前有一个调用:10: invokevirtual #29
。
它调用 public int Wrapper(int);
似乎什么也没做。 (虽然我可能是错的,因为我没有太多阅读字节码的经验)
那么问题来了,那个电话的意义是什么?不能省略吗?
问题是您的代码片段不等价。一个隐式的classFoo
是compiled/desugared到一个classFoo
和一个隐式的转换方法Foo
.这也是隐式 classes(当前)不能成为顶级的原因。
所以你的第一个片段应该是:
object Example1 {
class Wrapper(val self: Int) extends AnyVal {
def add(n: Int): Int = self + n
}
def Wrapper(self: Int): Wrapper = new Wrapper(self)
def foo(w: Wrapper): Wrapper = Wrapper(w.add(42))
}
如果可能,编译器会删除对值 class 构造函数的调用。但它不会删除对 Wrapper
方法 的调用,无论是否隐含。
我猜 JVM 中的 JIT 编译器无论如何最终都会删除该方法调用。
我正在检查由隐式 类 生成的字节码,并想与它们扩展 AnyVal
.
没有隐式:
object Example1 {
class Wrapper(val self: Int) extends AnyVal {
def add(n: Int): Int = self + n
}
def foo(w: Wrapper): Wrapper = new Wrapper(w.add(42))
}
字节码(的相关部分):
scala>:javap Example1
[...]
public int foo(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
0: getstatic #19 // Field Example1$Wrapper$.MODULE$:LExample1$Wrapper$;
3: iload_1
4: bipush 42
6: invokevirtual #23 // Method Example1$Wrapper$.add$extension:(II)I
9: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LExample1$;
0 10 1 w I
LineNumberTable:
line 11: 3
[...]
与implicit
:
object Example2 {
implicit class Wrapper(val self: Int) extends AnyVal {
def add(n: Int): Int = self + n
}
def foo(w: Wrapper): Wrapper = w.add(42)
}
字节码(的相关部分):
scala>:javap Example2
[...]
public int Wrapper(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=2
0: iload_1
1: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LExample2$;
0 2 1 self I
LineNumberTable:
line 9: 0
public int foo(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
0: aload_0
1: getstatic #23 // Field Example2$Wrapper$.MODULE$:LExample2$Wrapper$;
4: iload_1
5: bipush 42
7: invokevirtual #27 // Method Example2$Wrapper$.add$extension:(II)I
10: invokevirtual #29 // Method Wrapper:(I)I
13: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 this LExample2$;
0 14 1 w I
LineNumberTable:
line 12: 0
[...]
作为扩展 AnyVal
的结果,对伴随对象调用了对 add
的调用,并且类型 Wrapper
未显示在 [=20= 的类型签名中] (public int foo(int);
) 在两个版本中。
然而,在第二个版本中,return之前有一个调用:10: invokevirtual #29
。
它调用 public int Wrapper(int);
似乎什么也没做。 (虽然我可能是错的,因为我没有太多阅读字节码的经验)
那么问题来了,那个电话的意义是什么?不能省略吗?
问题是您的代码片段不等价。一个隐式的classFoo
是compiled/desugared到一个classFoo
和一个隐式的转换方法Foo
.这也是隐式 classes(当前)不能成为顶级的原因。
所以你的第一个片段应该是:
object Example1 {
class Wrapper(val self: Int) extends AnyVal {
def add(n: Int): Int = self + n
}
def Wrapper(self: Int): Wrapper = new Wrapper(self)
def foo(w: Wrapper): Wrapper = Wrapper(w.add(42))
}
如果可能,编译器会删除对值 class 构造函数的调用。但它不会删除对 Wrapper
方法 的调用,无论是否隐含。
我猜 JVM 中的 JIT 编译器无论如何最终都会删除该方法调用。