Scala和Java的混合:如何正确获取泛型构造函数参数?
我有一些遗留Java代码,它在我无法控制的地方定义了一个通用的Scala和Java的混合:如何正确获取泛型构造函数参数?,java,scala,generics,types,scala-java-interop,Java,Scala,Generics,Types,Scala Java Interop,我有一些遗留Java代码,它在我无法控制的地方定义了一个通用的有效负载变量(即,我无法更改其类型): 这会给出编译器警告“类型安全:构造函数…属于原始类型…” Scala案例类定义为: // Scala code case class ScalaMessage[T <: SomeBaseType](payload: Wrapper[T]) 并相应地称之为 // Java code doSomethingWith(payload) 但是我不能直接实例化包装器对象而不得到“raw typ
有效负载变量(即,我无法更改其类型):
这会给出编译器警告“类型安全:构造函数…属于原始类型…”
Scala案例类定义为:
// Scala code
case class ScalaMessage[T <: SomeBaseType](payload: Wrapper[T])
并相应地称之为
// Java code
doSomethingWith(payload)
但是我不能直接实例化包装器对象而不得到“raw type”警告。在这里,我需要使用静态辅助方法:
static <T> Wrapper<T> of(T value) {
return new Wrapper<T>(value);
}
解决方案
我可以向Scala companion对象添加类似的帮助器方法:
// 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
}
除非有人想出一个更优雅的解决方案,否则我将把它作为一个答案
谢谢大家! 我认为问题在于,在Java中,如果您执行以下操作:
ScalaMessage msg = new ScalaMessage(payload);
然后使用原始类型实例化ScalaMessage
。或者换句话说,您使用ScalaMessage
作为非泛型类型(当Java引入泛型时,它们保持将泛型类视为非泛型类的能力,主要是为了向后兼容)
实例化ScalaMessage
时,只需指定类型参数即可:
// (here T = MyDerivedType, where MyDerivedType must extend SomeBaseType
ScalaMessage<MyDerivedType> msg = new ScalaMessage<>(payload);
case class ScalaMessage[T<:SomeBaseType](payload: Wrapper[_ <: T])
然后用java将其实例化如下:
new ScalaMessage(payload)
这很有效。然而,现在ScalaMessage
不再是泛型的,如果您想将其用于更精细的薪资(比如Wrapper),这可能是一个问题。您遇到的事实是Java泛型实现得很差。您无法在Java中正确实现协方差和逆变,您必须使用通配符
case class ScalaMessage[T <: SomeBaseType](payload: Wrapper[T])
这正是方差的定义。因为Java中的泛型是不变的,所以该操作是非法的。唯一的解决方案是更改构造函数的签名
class ScalaMessage[T](wrapper:Wrapper[_<:T])
classscalamessage[T](wrapper:wrapper[\u)不起作用,因为我实际上有一个wrapper+1类型的方法参数来详细解释Java/Scala类型的系统
// (here T = MyDerivedType, where MyDerivedType must extend SomeBaseType
ScalaMessage<MyDerivedType> msg = new ScalaMessage<>(payload);
[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);
case class ScalaMessage(payload: Wrapper[_ <: SomeBaseType])
new ScalaMessage(payload)
case class ScalaMessage[T<:SomeBaseType](payload: Wrapper[_ <: T])
ScalaMessage<SomeBaseType> msg = new ScalaMessage<SomeBaseType>(payload);
case class ScalaMessage[T <: SomeBaseType](payload: Wrapper[T])
Wrapper[K]<:Wrapper[T] for K<:T
class ScalaMessage[T](wrapper:Wrapper[_<:T])
class Wrapper[+T]
class ScalaMessage[+T](wrapper:Wrapper[T])
object ScalaMessage {
class A
class B extends A
val myVal:Wrapper[_<:A] = new Wrapper[B]()
val message:ScalaMessage[A] = new ScalaMessage[A](myVal)
}