我遇到与Java interoperability woes with Scala generics and boxing相同的问题,但我不认为那里的解决方案对我有用,因为它需要修改第三方代码。
具体来说,来自Java (比如说MyJavaClass)我正在尝试扩展一个Scala类(MyScalaClass),它本身扩展了com.twitter.app.App。应用扩展了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类,我就可以解决这个问题(我想 CloseAwait)在原始方法实现中用[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,如果这是相关的:我真的不知道它为什么要求我实现{ {1}}首先。从MyScalaClass继承的Scala类不必实现该方法,并且构建得很好。
我该如何解决这个问题?谢谢你的演奏。
答案 0 :(得分:0)
你可以使用类似的技巧让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
。)
现在,使用扩展单元技巧,可能存在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 bug而不是一个scalac bug。不幸的是,它让你无法用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!