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.
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`
All that to say that when I finally get to extending MyScalaClass by writing MyJavaClass, I get
[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
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)
{}
Now I get
[ERROR] return type void is not compatible with scala.runtime.BoxedUnit
Changing my method to
public BoxedUnit result(com.twitter.util.Duration d,
com.twitter.util.Awaitable.CanAwait c)
{return BoxedUnit.UNIT;}
results in
[ERROR] return type scala.runtime.BoxedUnit is not compatible with void
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).
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.
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.)
The gory details are as follows.
The Twitter hierarchy can be simplified as follows:
Now, when you write your class, you don't just extend C. Instead, you
where
E
takes the place ofMyScalaClass
. (Andz
isresult
.)Now, with the extends-unit trick, all the signatures that Java could possibly want are present:
void z
is not abstract any more! In fact, nothing is.But javac still isn't happy.
Um...javac, yes it does?
And if we make
J
abstract itself, it explains what the real problem is: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.
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:
Then Scala:
Then Java again:
Which at leasts let you switch back and forth whenever you need to: