Scala泛型和装箱带来的Java互操作性问题
假设我有这个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
如果我尝试一个抽象类,它委托给super trait技巧:
abstract class Qux extends Baz {
override def x() = super.x()
}
然后:
public class JQux extends Qux {}
更糟糕的是:
^{10}pr$(注意,如果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 bug,但是有解决办法吗?我并不真正关心Baz
部分,但是有什么方法可以从Java中的Qux
继承吗
# 1 楼答案
它们不是scalac虫子;这是因为Scala编译器正在代表您努力研究过程和方法之间的差异,而Java编译器则不然
为了提高效率和Java兼容性,非泛型返回
Unit
的方法实际上被实现为过程(即返回类型为void
)。然后通过调用void
版本并返回BoxedUnit
来实现通用实现问题是,虽然javac将对特定的与泛型的
Object
派生返回类型执行相同的操作,但它不理解Object
-void
交叉这是一种解释。有一个变通方法,尽管它使Scala层次结构复杂化:
现在,您强迫斯卡拉考虑一些不完全-^ {CD1>}返回类型的可能性,因此它保留了{{< CD4}}作为返回;而且
Baz
抛弃了这种可能性,但它不会生成一个新的void x()
来混淆Java至少可以说,这是脆弱的。修复它可能是Java和Scala团队的一项工作,不过:只要有
BoxedUnit
版本,Java就不会高兴;它对void
版本非常恼火。(通过从Foo继承两次,您可以用这两种方法生成一个抽象类;因为它不起作用,所以细节并不重要。)Scala可能可以通过发出经过修改的字节码来单独实现这一点,该字节码在Java期望的任何地方都有一个额外的BoxedUnit方法。。。不确定