涉及 BoxedUnit/void 返回类型的 Java-Scala 互操作问题
Posted
技术标签:
【中文标题】涉及 BoxedUnit/void 返回类型的 Java-Scala 互操作问题【英文标题】:Java-Scala interop issue involving BoxedUnit/void return types 【发布时间】:2015-07-20 22:07:49 【问题描述】:我遇到了与Java interoperability woes with Scala generics and boxing 相同的问题,但我认为那里的解决方案不适合我,因为它需要修改第三方代码。
具体来说,来自 Java(比如 MyJavaClass)我正在尝试扩展一个本身扩展 com.twitter.app.App 的 Scala 类 (MyScalaClass)。应用扩展了com.twitter.util.CloseAwaitably,而这又扩展了com.twitter.util.Awaitable。
Awaitable // trait where `result` is defined
^-CloseAwaitably // trait with some impl of `result`
^-App // trait with no mention of `result`
^-MyScalaClass // abstract class with no mention of `result`
^-MyJavaClass // just trying to get this guy to compile
// and it expects an impl of `result`
这就是说,当我最终通过编写 MyJavaClass 来扩展 MyScalaClass 时,我明白了
[ERROR] MyJavaClass.java:[11,8] MyJavaClass 不是抽象的,而是 不覆盖抽象方法 结果(com.twitter.util.Duration,com.twitter.util.Awaitable.CanAwait) 在 com.twitter.util.Awaitable
我认为出于某种原因我只需要实现 result
,所以我在 MyJavaClass 中实现了:
public void result(com.twitter.util.Duration d,
com.twitter.util.Awaitable.CanAwait c)
现在我明白了
[ERROR] 返回类型 void 与 scala.runtime.BoxedUnit 不兼容
改变我的方法
public BoxedUnit result(com.twitter.util.Duration d,
com.twitter.util.Awaitable.CanAwait c)
return BoxedUnit.UNIT;
结果
[ERROR] 返回类型 scala.runtime.BoxedUnit 与 void 不兼容
这到底是怎么回事……所以我开始用谷歌搜索。 Java interoperability woes with Scala generics and boxing 的答案似乎是说 Scala 在我的情况下生成了与 Java 不兼容的字节码,如果我控制了一些 Twitter 类,我可以解决这个问题(我认为 CloseAwaitably)在原始方法实现中使用[U <: Unit]
和return ().asInstanceOf[U]
对其进行泛化,以欺骗scalac 承认“某些不完全单位类型”的可能性(尽管我并不完全清楚它是如何工作的)。
我无法控制 Twitter 类,只有 MyScalaClass 和 MyJavaClass。 我实际上并不关心 result
方法 - 我只想能够从 MyJavaClass 扩展 MyScalaClass,实现我在 MyScalaClass 中定义的一些抽象方法。对于它的价值,我正在使用 Scala 2.10.4、JDK 1.8 和 (twitter) util-core_2.10 6.22.0,如果这是相关的:我真的不知道为什么它需要我实现 @首先是 987654333@。从 MyScalaClass 继承的 Scala 类不必实现该方法,并且构建得很好。
我该如何解决这个问题?感谢您的参与。
【问题讨论】:
难道你不能编写一个 Scala 包装类来转发你在 Java 类中提供的其他方法吗? 也许吧,但到目前为止我在这个方向上的努力都没有结果。尝试从 Scala 覆盖result
总是会导致 error: method result overrides nothing.
我已经尝试了我能想到的所有签名损坏。此外,出于某种原因,从任何 Scala 子类实现 result
不是必要,这让我怀疑正在发生一些不正常的事情。
这看起来不是应该从子类中覆盖CloseAwaitably
中的result
吗? override def result(timeout: Duration)(permit: CanAwait): Unit = super.result(timeout)(permit);
显然没有。
如果我从开头删除override
并尝试重新编译,我会得到name *** between defined and inherited member
和...have same type after erasure
而不是method result overrides nothing
。
【参考方案1】:
您可以使用类似的技巧来使 Scala 的部分正确,但 javac 仍然会感到困惑,因为它正在寻找“错误”的东西而不是正确的东西。 (JVM 会寻找正确的东西,所以它运行良好。)
血淋淋的细节如下。
Twitter 层次结构可以简化如下:
package test
trait A[+Z] def z(): Z
trait B extends A[Unit] def z() = ()
trait C extends B
现在,当你编写你的类时,你不只是扩展 C。相反,你
package test
abstract class D[U <: Unit] extends A[U] with C
override def z: U = (this: B).z.asInstanceOf[U]
abstract class E extends D[Unit]
其中E
代替MyScalaClass
。 (而z
是result
。)
现在,使用 extends-unit 技巧,Java 可能需要的所有签名都存在:
$ javap test.B
public interface test.B extends test.A
public abstract void z();
$ javap test.D
public abstract class test.D extends java.lang.Object implements test.C
public scala.runtime.BoxedUnit z();
public java.lang.Object z();
public void z();
public test.D();
void z
不再是抽象的了!事实上,什么都没有。
但是javac 还是不开心。
package test;
public class J extends E
public J()
$ javac -cp /jvm/scala-library.jar:. J.java
J.java:3: test.J is not abstract and does not override
abstract method z() in test.B
嗯...javac,是吗?
如果我们将 J
抽象化,它就说明了真正的问题是什么:
J.java:3: z() in test.D cannot implement z() in test.B;
attempting to use incompatible return type
found : scala.runtime.BoxedUnit
required: void
也就是说,如果两个方法仅在返回类型上有所不同,并且不是 Java 认可的泛型 Object vs something-else,javac 将找不到正确的。
在我看来,这更像是一个 javac 错误,而不是一个 scalac 错误。不幸的是,它使您无法在 Java 中实现这些类。
作为一种解决方法,您可以(尴尬地)通过将两个类链接在一起来使用组合。
Java:
package test;
public interface I
public void doThing();
然后是 Scala:
package test
trait A[+Z] def z(): Z
trait B extends A[Unit] def z(): Unit = println("Ok")
trait C extends B
class D extends C
private var myI: I
def i = myI
def bind(newI: I) myI = newI
def doJavaThing = i.doThing
然后是 Java:
package test;
public class J implements I
public static D d;
public J(D myD) d = myD; d.bind(this);
public void doThing() System.out.println("Works!");
这至少可以让您在需要时来回切换:
scala> new test.J(new test.D)
res0: test.J = test.J@18170f98
scala> res0.doThing
Works!
scala> res0.asScala.doJavaThing
Works!
scala> res0.asScala.asJava.doThing
Works!
【讨论】:
你真的试过这个吗?我在 D 中得到了这个,method result overrides nothing
。我已经尝试使用我的实际 MyScalaClass 实现,以及您的精简版本(和名称),错误是一样的。
trait D[Z1 <: unit extends awaitable override def result duration permit: canawait z1="().asInstanceOf[Z1]" e app with d public class j>J is not abstract and does not override abstract method result(com.twitter.util.Duration,com.twitter.util.Awaitable.CanAwait) in com.twitter.util.CloseAwaitably 这很有趣,因为 CloseAwaitably 是您方案中的 B,扩展 A...
你可以忽略我的第一条评论,因为我刚刚没有用implicit
标记permit
。这样做之后,引用 CloseAwaitably 的第二条评论似乎是某种线索。我似乎已经从 Awaitable 中成功捕获了result
,只是未能在 CloseAwaitably 中捕获 result
。
@danielpcox - 你为什么要覆盖result
?它没有在任何地方定义。在我的示例中,我没有覆盖 z
。
result
不是在 CloseAwaitably 中定义的吗?如果我从上面删除override
,我会得到[ERROR] method result in trait CloseAwaitably of type (timeout: com.twitter.util.Duration)(implicit permit: com.twitter.util.Awaitable.CanAwait)Unit and [ERROR] method result in trait D of type (timeout: com.twitter.util.Duration)(implicit permit: com.twitter.util.Awaitable.CanAwait)Unit [ERROR] (Note: this can be resolved by declaring an override in class E.)
以上是关于涉及 BoxedUnit/void 返回类型的 Java-Scala 互操作问题的主要内容,如果未能解决你的问题,请参考以下文章