Scala 如何处理方法 return 类型中 Any 到 AnyVal 的协方差?
How does Scala handle Any to AnyVal covariance in method return types?
一例抵千言:
class A { def foo: Any = new Object }
class B extends A {
override def foo: AnyVal = 42
}
在Java中,甚至不允许使用签名@Override public int foo()
,B
中的重写方法foo
只能return包装器整数类型 (@Override java.lang.Integer foo()
).
Scala 是否能够在上面覆盖的 def foo: AnyVal
方法中避免 boxing/unboxing of AnyVal
值?
不,不是。 Scala 必须坚持发出正确的字节码:
λ scalac -Xprint:jvm Bar.scala
[[syntax trees at end of jvm]] // Bar.scala
package yuval.tests {
class A extends Object {
def foo(): Object = new Object();
def <init>(): yuval.tests.A = {
A.super.<init>();
()
}
};
class B extends yuval.tests.A {
override def foo(): Object = scala.Int.box(42);
def <init>(): yuval.tests.B = {
B.super.<init>();
()
}
}
}
您可以看到,虽然 AnyVal
在 Scala 中是允许的,但发出的 foo
的实际方法签名是 Object
而不是 AnyVal
,并且 Int
是盒装的。
Yuval 的回答可以概括:AnyVal
的擦除是 Object
(你可以看到这个,例如通过在 REPL 中输入 classOf[AnyVal]
),所以只要你有 AnyVal
在 Scala 中,你可以期待字节码中的 Object
。
例如如果您将 A
更改为
class A { def foo: AnyVal = 0 }
还是Object
。
也许有一些情况使用AnyVal
本身会避免装箱,但我会感到惊讶。它的创建几乎是为了编译器的方便,后来又有了另一个用途 (value classes),但它在用户代码中很少有用(除了定义值 类)。
一例抵千言:
class A { def foo: Any = new Object }
class B extends A {
override def foo: AnyVal = 42
}
在Java中,甚至不允许使用签名@Override public int foo()
,B
中的重写方法foo
只能return包装器整数类型 (@Override java.lang.Integer foo()
).
Scala 是否能够在上面覆盖的 def foo: AnyVal
方法中避免 boxing/unboxing of AnyVal
值?
不,不是。 Scala 必须坚持发出正确的字节码:
λ scalac -Xprint:jvm Bar.scala
[[syntax trees at end of jvm]] // Bar.scala
package yuval.tests {
class A extends Object {
def foo(): Object = new Object();
def <init>(): yuval.tests.A = {
A.super.<init>();
()
}
};
class B extends yuval.tests.A {
override def foo(): Object = scala.Int.box(42);
def <init>(): yuval.tests.B = {
B.super.<init>();
()
}
}
}
您可以看到,虽然 AnyVal
在 Scala 中是允许的,但发出的 foo
的实际方法签名是 Object
而不是 AnyVal
,并且 Int
是盒装的。
Yuval 的回答可以概括:AnyVal
的擦除是 Object
(你可以看到这个,例如通过在 REPL 中输入 classOf[AnyVal]
),所以只要你有 AnyVal
在 Scala 中,你可以期待字节码中的 Object
。
例如如果您将 A
更改为
class A { def foo: AnyVal = 0 }
还是Object
。
也许有一些情况使用AnyVal
本身会避免装箱,但我会感到惊讶。它的创建几乎是为了编译器的方便,后来又有了另一个用途 (value classes),但它在用户代码中很少有用(除了定义值 类)。