假设我具有以下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;
}
}
现在假设我x
在Scala端定义了一个实现:
trait Baz extends Foo[Unit] {
def x(): Unit = ()
}
我知道我x
从Java中看不到这一点,所以我定义了自己的:
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] ^
(请注意,JQux
如果Baz
不进行扩展,则此定义将很好用Foo[Unit]
。)
如果您查看javap
有关的内容Qux
,那就太奇怪了:
public abstract class Qux implements Baz {
public void x();
public java.lang.Object x();
public Qux();
}
我这里都认为存在的问题Baz
和Qux
必须scalac错误,但有一种解决方法吗?我并不真正在乎这一Baz
部分,但是有什么方法可以Qux
在Java中继承呢?
它们不是scalac bug。这是因为Scala编译器正在代表您努力工作,以阐明过程和方法之间的差异,而Java编译器则没有。
为了提高效率和Java兼容性,Unit
非一般返回的方法实际上被实现为过程(即return type为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
问题是,尽管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] {}
现在,您已迫使Scala考虑某种非精确Unit
返回类型的可能性,因此它会一直保持BoxedUnit
返回状态;并Baz
抛弃了这种可能性,但它不会产生void x()
使Java迷惑的新方法。
至少可以说这是脆弱的。修复它可能是在Java和Scala团队都工作,但:Java是不幸福的,只要BoxedUnit
版本是存在的; 它被void
版本激怒了。(您可以通过从Foo继承两次来生成两者的抽象类;因为它无法正常工作,细节并不重要。)Scala可以通过发出更改的字节码来单独完成它,该字节码在Java期望的任何地方都有额外的BoxedUnit方法。 ..没有把握。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句