I have some legacy Java code that defines a generic payload variable somewhere outside of my control (i.e. I can not change its type):
// Java code
Wrapper<? extends SomeBaseType> payload = ...
I receive such a payload value as a method parameter in my code and want to pass it on to a Scala case class (to use as message with an actor system), but do not get the definitions right such that I do not get at least a compiler warning.
// still Java code
ScalaMessage msg = new ScalaMessage(payload);
This gives a compiler warning “Type safety: contructor… belongs to raw type…”
The Scala case class is defined as:
// Scala code
case class ScalaMessage[T <: SomeBaseType](payload: Wrapper[T])
How can I define the case class such that the code compiles cleanly? (sadly, changing the code of the Java Wrapper class or the type of the payload parameter is not an option)
Updated to clarify the origin of the payload parameter
Added For comparison, in Java I can define a parameter just in the same way as the payload variable is defined:
// Java code
void doSomethingWith(Wrapper<? extends SomeBaseType> payload) {}
and call it accordingly
// Java code
doSomethingWith(payload)
But I can’t instantiate e.g. a Wrapper object directly without getting a “raw type” warning. Here, I need to use a static helper method:
static <T> Wrapper<T> of(T value) {
return new Wrapper<T>(value);
}
and use this static helper to instantiate a Wrapper object:
// Java code
MyDerivedType value = ... // constructed elsewhere, actual type is not known!
Wrapper<? extends SomeBaseType> payload = Wrapper.of(value);
Solution
I can add a similar helper method to a Scala companion object:
// Scala code
object ScalaMessageHelper {
def apply[T <: SomeBaseType](payload: Wrapper[T]) =
new ScalaMessage(payload)
}
object ScalaMessageHelper2 {
def apply[T <: SomeBaseType](payload: Wrapper[T]) =
ScalaMessage(payload) // uses implicit apply() method of case class
}
and use this from Java to instantiate the ScalaMessage class w/o problems:
// Java code
ScalaMessage msg = ScalaMessageHelper.apply(payload);
Unless someone comes up with a more elegant solution, I will extract this as an answer…
Thank you!
I think the problem is that in Java if you do the following:
Then you are instantiating
ScalaMessageusing its raw type. Or in other words, you useScalaMessageas a non generic type (when Java introduced generics, they kept the ability to treat a generic class as a non-generic one, mostly for backward compatibility).You should simply specify the type parameters when instantiating
ScalaMessage:UPDATE: After seeing your comment, I actually tried it in a dummy project, and I actually get an error:
It seems like a mismatch between java generics (that we can emulate through exitsentials in scala ) and scala generics. You can fix this by just dropping the type parameter in
ScalaMessageand using existentials instead:and then instantiate it in java like this:
This works. However, now
ScalaMessageis not generic anymore, which might be a problem if you want use it with more refined paylods (say aWrapper<? extends MyDerivedType>).To fix this, let’s do yet another small change to
ScalaMessage:And then in java:
Problem solved 🙂