涉及BoxedUnit/void返回类型的Java-Scala互操作问题 [英] Java-Scala interop issue involving BoxedUnit/void return types

查看:107
本文介绍了涉及BoxedUnit/void返回类型的Java-Scala互操作问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到的问题与 Scala泛型和拳击技术在Java互操作性上的问题一样,但是我不认为那里的解决方案将对我有用,因为它需要修改第三方代码.

I have the same problem as in Java interoperability woes with Scala generics and boxing, but I don't think the solution there will work for me because it would require a modification to third-party code.

特别是来自Java (例如MyJavaClass),我正在尝试扩展本身扩展 com.twitter.util.CloseAwaitably ,然后依次扩展 com.twitter.util.Awaitable .

Specifically, from Java (say MyJavaClass) I'm trying to extend a Scala class (MyScalaClass) that itself extends 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时,我得到了

All that to say that when I finally get to extending MyScalaClass by writing MyJavaClass, I get

[错误] MyJavaClass.java:[11,8] MyJavaClass不是抽象的,但确实不覆盖抽象方法结果(com.twitter.util.Duration,com.twitter.util.Awaitable.CanAwait)在com.twitter.util.Awaitable

[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中做到了:

I figured I just have to implement result for some reason, so I do, in MyJavaClass:

public void result(com.twitter.util.Duration d,
                   com.twitter.util.Awaitable.CanAwait c)
{}

现在我得到

[错误]返回类型void与scala.runtime.BoxedUnit不兼容

[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;}

产生

[错误]返回类型scala.runtime.BoxedUnit与void不兼容

[ERROR] return type scala.runtime.BoxedUnit is not compatible with void

该死的...所以我开始谷歌搜索. Java互操作性因Scala泛型和拳击而陷入困境似乎表示Scala在我所处的情况下生成了Java不兼容的字节码,而我如果我可以控制某些Twitter类(我认为 CloseAwaitably)以使用 [U< :: Unit] return将其通用化,则可以解决该问题().asInstanceOf [U] 在原始方法的实现中,诱使scalac承认某些非精确单位类型"的可能性(尽管我尚不清楚这是如何工作的).

What the heck... So then I start googling. The answer to Java interoperability woes with Scala generics and boxing seems to say that Scala generates Java-incompatible bytecode in my situation, and that I could hack around the problem if I had control of some of the Twitter classes (I think CloseAwaitably) to genericize it with [U <: Unit] and return ().asInstanceOf[U] in the original method implementation to trick scalac into admitting the possibility of "some not-exactly-Unit type" (though I'm not totally clear on how this works).

我无法控制Twitter类,只有MyScalaClass和MyJavaClass.我实际上并不在意 结果 方法-我只是想能够从MyJavaClass扩展MyScalaClass,实现我定义的一些抽象方法在MyScalaClass中.对于它的价值,我使用的是Scala 2.10.4,JDK 1.8和(twitter)的util-core_2.10 6.22.0,如果这是相关的:我真的不知道为什么它要求我实现<首先是code> result .从MyScalaClass继承的Scala类不必实现该方法,并且可以很好地构建.

I don't have control over the Twitter classes, only MyScalaClass and MyJavaClass. I don't actually care about the result method - I just want to be able to extend MyScalaClass from MyJavaClass, implementing some abstract methods I defined in MyScalaClass. For what it's worth, I'm using Scala 2.10.4, JDK 1.8, and (twitter) util-core_2.10 6.22.0, and in case this is relevant: I don't really know why it's requiring me to implement result in the first place. A Scala class inheriting from MyScalaClass doesn't have to implement that method, and builds just fine.

我该如何解决?感谢您的播放.

How can I get around this? Thanks for playing.

推荐答案

您可以使用类似的技巧来使Scala的零件正确,但是javac仍然会感到困惑,因为它正在寻找错误"的东西,而不是正确的东西..(JVM寻找正确的东西,因此可以正常运行.)

You can use a similar trick to get Scala's part right, but javac still gets confused because it's looking for things that are "wrong" rather than those that are right. (The JVM looks for what is right, so it runs fine.)

血腥细节如下.

Twitter层次结构可以简化如下:

The Twitter hierarchy can be simplified as follows:

package test

trait A[+Z] { def z(): Z }
trait B extends A[Unit] { def z() = () }
trait C extends B {}

现在,当您编写类时,您不仅可以扩展C.相反,您可以

Now, when you write your class, you don't just extend C. Instead, you

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可能想要的所有签名都出现了:

Now, with the extends-unit trick, all the signatures that Java could possibly want are present:

$ 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 不再是抽象的!实际上,什么都不是.

void z is not abstract any more! In fact, nothing is.

但是javac still 并不满意.

But javac still isn't happy.

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,是吗?

Um...javac, yes it does?

如果我们使 J 本身成为抽象,它说明了真正的问题是什么

And if we make J abstract itself, it explains what the real problem is:

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认可的通用对象与其他对象,则javac将找不到正确的方法.

That is, if two methods differ only by return type, and it's not the Java-sanctioned generic Object vs something-else, javac won't find the correct one.

在我看来,这比一个scalac错误更像一个javac错误.不幸的是,这使您无法使用Java实现类.

This looks to me like a javac bug more than a scalac one. Unfortunately, it leaves you unable to implement the classes in Java.

作为解决方法,您可以通过将两个类链接在一起来(笨拙地)使用组合.

As a workaround, you can (awkwardly) use composition by linking two classes together.

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:

Then Java again:

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!"); }
}

至少可以让您在需要时来回切换:

Which at leasts let you switch back and forth whenever you need to:

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!

这篇关于涉及BoxedUnit/void返回类型的Java-Scala互操作问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆