Tra*_*own 8 java generics boxing scala
假设我有这个Scala特征:
trait UnitThingy {
def x(): Unit
}
Run Code Online (Sandbox Code Playgroud)
提供Java实现很容易:
import scala.runtime.BoxedUnit;
public class JUnitThingy implements UnitThingy {
public void x() {
return;
}
}
Run Code Online (Sandbox Code Playgroud)
现在让我们从一般特征开始:
trait Foo[A] {
def x(): A
}
trait Bar extends Foo[Unit]
Run Code Online (Sandbox Code Playgroud)
上面的方法不起作用,因为单位x返回现在已装箱,但解决方法很简单:
import scala.runtime.BoxedUnit;
public class JBar implements Bar {
public BoxedUnit x() {
return BoxedUnit.UNIT;
}
}
Run Code Online (Sandbox Code Playgroud)
现在假设我已经x在Scala端定义了一个实现:
trait Baz extends Foo[Unit] {
def x(): Unit = ()
}
Run Code Online (Sandbox Code Playgroud)
我知道我不能x从Java 看到这个,所以我定义了自己的:
import scala.runtime.BoxedUnit;
public class JBaz implements Baz {
public BoxedUnit x() {
return BoxedUnit.UNIT;
}
}
Run Code Online (Sandbox Code Playgroud)
但是这种情况爆发了:
[error] .../JBaz.java:3: error: JBaz is not abstract and does not override abstract method x() in Baz
[error] public class JBaz implements Baz {
[error] ^
[error] /home/travis/tmp/so/js/newsutff/JBaz.java:4: error: x() in JBaz cannot implement x() in Baz
[error] public BoxedUnit x() {
[error] ^
[error] return type BoxedUnit is not compatible with void
Run Code Online (Sandbox Code Playgroud)
如果我尝试抽象类 - 委托 - 超 - 特质技巧:
abstract class Qux extends Baz {
override def x() = super.x()
}
Run Code Online (Sandbox Code Playgroud)
然后:
public class JQux extends Qux {}
Run Code Online (Sandbox Code Playgroud)
更糟糕的是:
[error] /home/travis/tmp/so/js/newsutff/JQux.java:1: error: JQux is not abstract and does not override abstract method x() in Foo
[error] public class JQux extends Qux {}
[error] ^
Run Code Online (Sandbox Code Playgroud)
(请注意,JQux如果Baz没有扩展,这个定义会正常工作Foo[Unit].)
如果你看看javap有关的内容Qux,那就太奇怪了:
public abstract class Qux implements Baz {
public void x();
public java.lang.Object x();
public Qux();
}
Run Code Online (Sandbox Code Playgroud)
我认为这里的问题既Baz与Qux必须scalac错误,但有一种解决方法吗?我真的不关心这个Baz部分,但是我有什么办法可以继承QuxJava吗?
它们不是scalac bug; 这就是Scala编译器正在努力代表您解决过程和方法之间的差异,而Java编译器却没有.
为了提高效率和Java兼容性,Unit非泛型返回的方法实际上是作为过程实现的(即返回类型是void).然后通过调用void版本并返回来实现泛型实现BoxedUnit.
public abstract class Qux implements Baz {
public void x();
Code:
0: aload_0
1: invokestatic #17 // Method Baz$class.x:(LBaz;)V
4: return
public java.lang.Object x();
Code:
0: aload_0
1: invokevirtual #22 // Method x:()V
4: getstatic #28 // Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit;
7: areturn
Run Code Online (Sandbox Code Playgroud)
问题是虽然javac会为特定的和泛型的Object返回类型做同样的事情,但它不理解Object- void交叉.
这是一个解释.有一种解决方法,但它使Scala层次结构复杂化:
trait Bazz[U <: Unit] extends Bar[Unit] {
def x() = ().asInstanceOf[U] // Must go here, not in Baz!
}
trait Baz extends Bazz[Unit] {}
Run Code Online (Sandbox Code Playgroud)
现在你已经强迫Scala考虑一些不完全Unit返回类型的可能性,所以它保留BoxedUnit了返回; 并Baz抛弃了这种可能性,但它并没有产生新void x()的混淆Java.
至少可以说,这很脆弱.但是,修复它可能对Java和Scala团队来说都是一项工作:只要版本存在,Java 就不开心BoxedUnit; 它受到void版本的积极骚扰.(你可以通过从Foo继承两次来生成一个抽象类;因为它不起作用,细节是不重要的.)Scala可能能够通过发出改进的字节码来单独完成它,该字节码在Java期望的地方有一个额外的BoxedUnit方法. ..不确定.