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
这个定义会正常工作。)
如果你看一下javap
对Qux
,那就太奇怪了:
public abstract class Qux implements Baz { public void x(); public java.lang.Object x(); public Qux(); }
我认为Baz
和Qux
的问题都是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返回类型为您做同样的事情,但它不理解Object
– void
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方法。 ..不确定。