Java互操作性与Scalagenerics和装箱相关

假设我有这个Scala特征:

trait UnitThingy { def x(): Unit } 

提供Java实现很容易:

 import scala.runtime.BoxedUnit; public class JUnitThingy implements UnitThingy { public void x() { return; } } 

现在让我们从一般特征开始:

 trait Foo[A] { def x(): A } trait Bar extends Foo[Unit] 

上面的方法不起作用,因为单位x返回现在已装箱,但解决方法很简单:

 import scala.runtime.BoxedUnit; public class JBar implements Bar { public BoxedUnit x() { return BoxedUnit.UNIT; } } 

现在假设我在Scala端有一个x定义的实现:

 trait Baz extends Foo[Unit] { def x(): Unit = () } 

我知道我无法从Java中看到这个x ,所以我定义了自己的:

 import scala.runtime.BoxedUnit; public class JBaz implements Baz { public BoxedUnit x() { return BoxedUnit.UNIT; } } 

但是这种情况爆发了:

 [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 

如果我尝试抽象类 – 委托 – 超 – 特质技巧:

 abstract class Qux extends Baz { override def x() = super.x() } 

然后:

 public class JQux extends Qux {} 

更糟糕的是:

 [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] ^ 

(注意,如果Baz没有扩展Foo[Unit]JQux这个定义会正常工作。)

如果你看一下javapQux ,那就太奇怪了:

 public abstract class Qux implements Baz { public void x(); public java.lang.Object x(); public Qux(); } 

我认为BazQux的问题都是scalac错误,但有没有解决方法? 我真的不关心Baz部分,但是有什么方法可以inheritanceJava中的Qux吗?

它们不是scalac bug; 这就是Scala编译器正在努力代表您解决过程和方法之间的差异,而Java编译器却没有。

为了提高效率和Java兼容性,非generics返回Unit方法实际上是作为过程实现的(即返回类型为void )。 然后通过调用void版本并返回BoxedUnit来实现generics实现。

 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 

问题是虽然javac将使用特定的与通用的Object -derived返回类型为您做同样的事情,但它不理解Objectvoid crossover。

这是一个解释。 有一种解决方法,但它使Scala层次结构复杂化:

 trait Bazz[U <: Unit] extends Bar[Unit] { def x() = ().asInstanceOf[U] // Must go here, not in Baz! } trait Baz extends Bazz[Unit] {} 

现在你已经强制Scala考虑一些不完全Unit返回类型的可能性,所以它保留了BoxedUnit的返回; 而且Baz抛弃了这种可能性,但它并没有产生一个新的void x()来混淆Java。

至少可以说,这很脆弱。 但是,修复它可能是Java和Scala团队的工作:只要BoxedUnit版本存在,Java 就不开心; void版本让它变得非常恼火。 (你可以通过从Fooinheritance两次来生成一个抽象类;因为它不起作用,细节是不重要的。)Scala可能能够通过发出改进的字节码来单独完成它,该字节码在Java期望的地方有一个额外的BoxedUnit方法。 ..不确定。