混合Scala和Java:如何获得通用类型的构造函数参数吧?

Eri*_*ner 6 java generics types scala scala-java-interop

我有一些遗留Java代码,它在我的控制之外的某处定义了一个泛型payload变量(即我不能改变它的类型):

// Java code
Wrapper<? extends SomeBaseType> payload = ...
Run Code Online (Sandbox Code Playgroud)

我在我的代码中收到了一个payload像方法参数这样的值,并希望将它传递给Scala case class (用作带有actor系统的消息),但是没有得到正确的定义,这样我至少得不到编译器警告.

// still Java code
ScalaMessage msg = new ScalaMessage(payload);
Run Code Online (Sandbox Code Playgroud)

这给出了编译器警告"类型安全:构造函数...属于原始类型......"

Scala case class定义为:

// Scala code
case class ScalaMessage[T <: SomeBaseType](payload: Wrapper[T]) 
Run Code Online (Sandbox Code Playgroud)

如何定义案例类以使代码干净地编译?(遗憾的是,更改Java Wrapper类的代码或payload参数的类型不是一个选项)

更新以阐明有效负载参数的来源

添加为了比较,在Java中,我可以像定义payload变量一样定义参数:

// Java code
void doSomethingWith(Wrapper<? extends SomeBaseType> payload) {}
Run Code Online (Sandbox Code Playgroud)

并相应地调用它

// Java code
doSomethingWith(payload)
Run Code Online (Sandbox Code Playgroud)

但我不能直接实例化一个Wrapper对象而不会得到"原始类型"警告.在这里,我需要使用static辅助方法:

static <T> Wrapper<T> of(T value) {
   return new Wrapper<T>(value);
}
Run Code Online (Sandbox Code Playgroud)

并使用此静态助手实例化Wrapper对象:

// Java code
MyDerivedType value = ... // constructed elsewhere, actual type is not known!
Wrapper<? extends SomeBaseType> payload = Wrapper.of(value);
Run Code Online (Sandbox Code Playgroud)

我可以为Scala伴侣对象添加一个类似的辅助方法:

// 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
}
Run Code Online (Sandbox Code Playgroud)

并使用Java中的这个实例化没有ScalaMessage问题的类:

// Java code
ScalaMessage msg = ScalaMessageHelper.apply(payload);
Run Code Online (Sandbox Code Playgroud)

除非有人提出更优雅的解决方案,否则我会将其作为答案提取......

谢谢!

Rég*_*les 3

我认为问题是在Java中如果你执行以下操作:

ScalaMessage msg = new ScalaMessage(payload);
Run Code Online (Sandbox Code Playgroud)

然后您将ScalaMessage使用其原始类型进行实例化。或者换句话说,您将其用作ScalaMessage非泛型类型(当 Java 引入泛型时,他们保留了将泛型类视为非泛型类的能力,主要是为了向后兼容)。

您应该在实例化时简单地指定类型参数ScalaMessage

// (here T = MyDerivedType, where MyDerivedType must extend SomeBaseType
ScalaMessage<MyDerivedType> msg = new ScalaMessage<>(payload);
Run Code Online (Sandbox Code Playgroud)

更新:看到您的评论后,我实际上在一个虚拟项目中尝试了它,并且实际上收到了一个错误:

[error] C:\Code\sandbox\src\main\java\bla\Test.java:8: cannot find symbol
[error] symbol  : constructor ScalaMessage(bla.Wrapper<capture#64 of ? extends bla.SomeBaseType>)
[error] location: class test.ScalaMessage<bla.SomeBaseType>
[error]     ScalaMessage<SomeBaseType> msg = new ScalaMessage<SomeBaseType>(payload);
Run Code Online (Sandbox Code Playgroud)

java 泛型(我们可以通过 scala 中的 exitsentials 来模拟)和 scala 泛型之间似乎不匹配。您可以通过删除类型参数ScalaMessage并使用存在来解决此问题:

case class ScalaMessage(payload: Wrapper[_ <: SomeBaseType]) 
Run Code Online (Sandbox Code Playgroud)

然后在java中实例化它,如下所示:

new ScalaMessage(payload)
Run Code Online (Sandbox Code Playgroud)

这有效。然而, nowScalaMessage不再是通用的,如果您想将它与更精致的 Paylod 一起使用(例如 a ),这可能会成为问题Wrapper<? extends MyDerivedType>

为了解决这个问题,让我们再做一个小改动ScalaMessage

case class ScalaMessage[T<:SomeBaseType](payload: Wrapper[_ <: T]) 
Run Code Online (Sandbox Code Playgroud)

然后在java中:

ScalaMessage<SomeBaseType> msg = new ScalaMessage<SomeBaseType>(payload);
Run Code Online (Sandbox Code Playgroud)

问题解决了 :)