【发布时间】:2020-01-25 20:19:38
【问题描述】:
我正在尝试构建一个具有Contexts(保存不可变数据)和Modules 层次结构的框架,用于创建与数据一起工作的演员。 Context 上的子类包含更多数据(例如,RemoteContext 将包含有关如何与远程主机通信的信息)。还有Factory对象来制作相关对象,这是Context中数据的一部分。
我可以用子类定义层次结构,而且效果很好。定义完所有内容后,Mediator 对象会通过传递上下文来初始化每个 Module。
下面的代码显示了基本结构。
import java.{ util => ju}
trait Factory
trait Context[F <: Factory]
trait SomeContext[F <: Factory] extends Context[F]
trait MediatorModule[C <: Context[_ <: Factory]] {
def loadModule(c: C)
}
trait Mediator[C <: Context[Factory]] {
val context: C
def getModules: ju.List[MediatorModule[_ >: C]]
def run() = getModules.forEach(_.loadModule(context))
}
trait OtherFact extends Factory
trait OtherContext extends SomeContext[OtherFact]
class SomeModule extends MediatorModule[SomeContext[Factory]] {
def loadModule(c: SomeContext[Factory]): Unit = { }
}
class OtherModule extends MediatorModule[OtherContext] {
def loadModule(c: OtherContext): Unit = { }
}
class OtherContextImpl extends OtherContext {
}
class OtherMediator extends Mediator[OtherContext] {
val context: OtherContext = new OtherContextImpl
def getModules: ju.List[MediatorModule[_ >: OtherContext]] =
ju.Arrays.asList(new SomeModule,
new OtherModule)
}
(代码最初是用 Java 编写的,这就是它使用 Java 列表的原因)。
正如所写,编译失败:
Test.scala:78:26: type mismatch;
[error] found : SomeModule
[error] required: MediatorModule[_ >: OtherContext]
[error] Note: SomeContext[Factory] <: Any (and SomeModule <: MediatorModule[SomeContext[Factory]]), but trait MediatorModule is invariant in type C.
[error] You may wish to define C as +C instead. (SLS 4.5)
[error] ju.Arrays.asList(new SomeModule,
[error] ^
[error] one error found
按照编译器的建议声明 trait MediatorModule[+C <: Context[_ <: Factory]] 会产生两个错误:
Test.scala:52:20: covariant type C occurs in contravariant position in type C of value c
[error] def loadModule(c: C)
[error] ^
[error] Test.scala:75:29: type arguments [OtherContext] do not conform to trait Mediator's type parameter bounds [C <: Context[Factory]]
[error] class OtherMediator extends Mediator[OtherContext] {
[error] ^
我可以用 trait Context[+F <: Factory] 修复一个,但 co/contra 变体错误仍然存在。
我能做些什么来修复这个错误?另外,+C 是如何翻译回 Java 的?
【问题讨论】:
-
不用看太多,通常你用
def loadModule[C1 >: C](c: C1)解决那些,这样你就翻转了C的方差。我建议您首先阅读有关方差的内容,它是什么意思以及为什么它很重要。最后,+C无论如何都不会转换为 Java,JVM 由于类型擦除和其他原因无法管理差异。所有这些信息仅由 Scala 编译器使用,以避免在 Java 上很容易创建的一些错误。 -
@Luis Miguel Mejía Suárez 虽然
+C确实不能翻译成Java,但你后来所说的不是真的。 Scala 和 Java 都依赖于相同的 JVM 并具有相同的类型擦除机制。但问题是,Java 语言只支持 call-site 或 use-site 变体,例如Foo<? extends Integer> covariantFoo = new Foo<Integer>();。 Scala 支持这一点(如[_ <: Integer]),但也支持 declaration-site 差异,例如Foo[+C],表示Foo在 C 中总是协变的。 -
您的建议解决了示例代码中的问题。不幸的是,在生产代码中,相关部分都是Java,所以我可能无法进行相应的更改。
-
@slouc 是的,但同样只适用于编译器。 JVM 一般不支持方差的概念,也不支持类型参数(一般来说这很好)。
-
@slouc 是的,我不确定你是看错了还是我写的不好(因为英语不是我的母语)。但是,我(试图)说没有差异的是 JVM 而不是 Java 作为一种语言 (但后者确实没有那么强大比 Scala,它允许一些错误,特别是
Arrays)。
标签: scala generics covariance scala-java-interop existential-type