为什么隐式值 类 有一个额外的方法调用?

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 编译器无论如何最终都会删除该方法调用。