【问题标题】:Mixing contravariant and covariant types in Scala在 Scala 中混合逆变和协变类型
【发布时间】: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 &lt;: Context[_ &lt;: 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 &lt;: Factory] 修复一个,但 co/contra 变体错误仍然存​​在。

我能做些什么来修复这个错误?另外,+C 是如何翻译回 Java 的?

【问题讨论】:

  • 不用看太多,通常你用def loadModule[C1 &gt;: 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&lt;? extends Integer&gt; covariantFoo = new Foo&lt;Integer&gt;();。 Scala 支持这一点(如 [_ &lt;: Integer]),但也支持 declaration-site 差异,例如Foo[+C],表示Foo 在 C 中总是协变的。
  • 您的建议解决了示例代码中的问题。不幸的是,在生产代码中,相关部分都是Java,所以我可能无法进行相应的更改。
  • @slouc 是的,但同样只适用于编译器。 JVM 一般不支持方差的概念,也不支持类型参数(一般来说这很好)。
  • @slouc 是的,我不确定你是看错了还是我写的不好(因为英语不是我的母语)。但是,我(试图)说没有差异的是 JVM 而不是 Java 作为一种语言 (但后者确实没有那么强大比 Scala,它允许一些错误,特别是 Arrays)。

标签: scala generics covariance scala-java-interop existential-type


【解决方案1】:

尝试使 Context 和 SomeContext 协变

trait Context[+F <: Factory]

trait SomeContext[+F <: Factory] extends Context[F]

然后代码编译。

【讨论】:

    猜你喜欢
    • 2013-11-29
    • 2015-02-09
    • 1970-01-01
    • 2012-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多