Java-涉及 BoxedUnit/void return 类型的 Scala 互操作问题
Java-Scala interop issue involving BoxedUnit/void return types
我遇到了与 Java interoperability woes with Scala generics and boxing 中相同的问题,但我认为那里的解决方案对我不起作用,因为它需要修改第三方代码。
具体来说,来自 Java(比如我的JavaClass)我正在尝试扩展 Scala class (MyScalaClass) 本身扩展 com.twitter.app.App. App extends com.twitter.util.CloseAwaitably, and this in turn extends 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 is not abstract and does
not override abstract method
result(com.twitter.util.Duration,com.twitter.util.Awaitable.CanAwait)
in com.twitter.util.Awaitable
我认为出于某种原因我只需要实现 result
,所以我在 MyJavaClass:
中实现了
public void result(com.twitter.util.Duration d,
com.twitter.util.Awaitable.CanAwait c)
{}
现在我明白了
[ERROR] return type void is not compatible with scala.runtime.BoxedUnit
将我的方法更改为
public BoxedUnit result(com.twitter.util.Duration d,
com.twitter.util.Awaitable.CanAwait c)
{return BoxedUnit.UNIT;}
结果
[ERROR] return type scala.runtime.BoxedUnit is not compatible with void
到底是什么...然后我开始谷歌搜索。 Java interoperability woes with Scala generics and boxing 的答案似乎是说 Scala 在我的情况下会生成 Java 不兼容的字节码,如果我能控制某些 Twitter classes (我 认为 CloseAwaitably) 在原始方法实现中用 [U <: Unit]
和 return ().asInstanceOf[U]
泛化它以欺骗 scalac 承认 "some not-exactly-Unit type" 的可能性(尽管我我不太清楚这是如何工作的)。
我无法控制 Twitter classes,只有 MyScalaClass 和 MyJavaClass。 我实际上并不关心 result
方法 - 我只是希望能够扩展 MyScalaClass MyJavaClass,实现了我在 MyScalaClass 中定义的一些抽象方法。对于它的价值,我使用的是 Scala 2.10.4、JDK 1.8 和 (twitter) util-core_2.10 6.22.0,如果这是相关的:我真的不知道为什么它要求我首先实施 result
。从 MyScalaClass 继承的 Scala class 不必实现该方法,并且构建得很好。
我该如何解决这个问题?感谢您的参与。
您可以使用类似的技巧使 Scala 的部分正确,但 javac 仍然会感到困惑,因为它正在寻找 "wrong" 而不是正确的东西。 (JVM 寻找正确的东西,所以它运行良好。)
血淋淋的细节如下。
Twitter 层次结构可以简化如下:
package test
trait A[+Z] { def z(): Z }
trait B extends A[Unit] { def z() = () }
trait C extends B {}
现在,当您编写 class 时,您不只是扩展 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
也就是说,如果两个方法仅在 return 类型上有所不同,并且不是 Java 认可的泛型对象与其他对象,javac 将找不到正确的方法。
在我看来,这更像是 javac 错误,而不是 scalac 错误。不幸的是,它使您无法在 Java.
中实现 classes
作为解决方法,您可以(笨拙地)通过将两个 class 链接在一起来使用组合。
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!
我遇到了与 Java interoperability woes with Scala generics and boxing 中相同的问题,但我认为那里的解决方案对我不起作用,因为它需要修改第三方代码。
具体来说,来自 Java(比如我的JavaClass)我正在尝试扩展 Scala class (MyScalaClass) 本身扩展 com.twitter.app.App. App extends com.twitter.util.CloseAwaitably, and this in turn extends 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 is not abstract and does not override abstract method result(com.twitter.util.Duration,com.twitter.util.Awaitable.CanAwait) in com.twitter.util.Awaitable
我认为出于某种原因我只需要实现 result
,所以我在 MyJavaClass:
public void result(com.twitter.util.Duration d,
com.twitter.util.Awaitable.CanAwait c)
{}
现在我明白了
[ERROR] return type void is not compatible with scala.runtime.BoxedUnit
将我的方法更改为
public BoxedUnit result(com.twitter.util.Duration d,
com.twitter.util.Awaitable.CanAwait c)
{return BoxedUnit.UNIT;}
结果
[ERROR] return type scala.runtime.BoxedUnit is not compatible with void
到底是什么...然后我开始谷歌搜索。 Java interoperability woes with Scala generics and boxing 的答案似乎是说 Scala 在我的情况下会生成 Java 不兼容的字节码,如果我能控制某些 Twitter classes (我 认为 CloseAwaitably) 在原始方法实现中用 [U <: Unit]
和 return ().asInstanceOf[U]
泛化它以欺骗 scalac 承认 "some not-exactly-Unit type" 的可能性(尽管我我不太清楚这是如何工作的)。
我无法控制 Twitter classes,只有 MyScalaClass 和 MyJavaClass。 我实际上并不关心 result
方法 - 我只是希望能够扩展 MyScalaClass MyJavaClass,实现了我在 MyScalaClass 中定义的一些抽象方法。对于它的价值,我使用的是 Scala 2.10.4、JDK 1.8 和 (twitter) util-core_2.10 6.22.0,如果这是相关的:我真的不知道为什么它要求我首先实施 result
。从 MyScalaClass 继承的 Scala class 不必实现该方法,并且构建得很好。
我该如何解决这个问题?感谢您的参与。
您可以使用类似的技巧使 Scala 的部分正确,但 javac 仍然会感到困惑,因为它正在寻找 "wrong" 而不是正确的东西。 (JVM 寻找正确的东西,所以它运行良好。)
血淋淋的细节如下。
Twitter 层次结构可以简化如下:
package test
trait A[+Z] { def z(): Z }
trait B extends A[Unit] { def z() = () }
trait C extends B {}
现在,当您编写 class 时,您不只是扩展 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
也就是说,如果两个方法仅在 return 类型上有所不同,并且不是 Java 认可的泛型对象与其他对象,javac 将找不到正确的方法。
在我看来,这更像是 javac 错误,而不是 scalac 错误。不幸的是,它使您无法在 Java.
中实现 classes作为解决方法,您可以(笨拙地)通过将两个 class 链接在一起来使用组合。
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!