代码之家  ›  专栏  ›  技术社区  ›  Lukas Neukom

覆盖子类型中的差异

  •  10
  • Lukas Neukom  · 技术社区  · 10 年前

    有人能解释一下为什么示例1可以编译,而示例2不能编译吗?

    示例1:

    trait Foo[+A]
    trait Bar[A] extends Foo[A]
    

    示例2:

    trait Foo[A[+_]]
    trait Bar[A[_]] extends Foo[A]
    

    示例2未编译,并显示以下错误消息:“类型参数(A)的类型不符合trait Foo中的类型参数(类型A)的预期类型。A的类型参数与类型A的预期参数不匹配:类型_(in trait Bar)是不变的,但类型_(在trait Foo中)被声明为协变的“

    2 回复  |  直到 10 年前
        1
  •  7
  •   Didier Dupont    10 年前

    在实施例1中 +A 不是对A的约束 。任何类型都可以是Foo的参数。这是对 Foo 。这意味着在 食品 , A 只能出现在协变位置(很快,它可能是一个方法结果,但不是一个方法参数)。

    Bar 非共变意味着Bar的接口不满足相同的约束(或者至少不公布它),因此可能在 酒吧 ,具有 A. 已添加参数。这很常见。例如 collection.Seq[+A] ,并扩展为 collection.mutable.Seq[A] 。这不会造成无声问题。如果 Y <: X Bar[Y] 不是 Bar[X] ,但它仍然是 Foo[X] .

    另一方面,在示例2中 A[+_] 是对的约束 A. 。只有具有协变类型参数的类型可以是Foo的参数。的代码 食品 可能会利用该约束,例如 A[String] A[Any] 代码中的某个位置 食品 .

    然后允许 酒吧 用非协变类型来实例化是不合理的。从继承的代码 食品 仍然可以调用并分配 A[字符串] A[任何] 什么时候 A. 不再是协变的。

    只要允许解除对泛型参数的约束,就会出现非常类似的可靠性问题。假设你有 Foo[X <: Ordered[X]] (或 Foo[X : ordering] )你有一个方法 sort 在里面 食品 。那么假设 Bar[X] extends Foo 允许。也许在代码中没有 酒吧 要求 X 但仍然, 分类 将是可调用的,并且肯定会对无法订购的项目产生错误行为。


    关于Traversable[+Elem,+Col[+_]]注释中的问题,并将其扩展为一个可变类:

    从技术上讲,是的,你可以扩展它,放一些 var youCanMutateThat : Elem = _ 在…内我想这不是你想要的。但是我想您的计划是允许使用带有可变Col的扩展,并且我认为您无法做到这一点,原因如上所述。但是,为什么首先要使用Col[+_]约束?

        2
  •  2
  •   Yuriy    10 年前

    在实施例2中,需要为A[_]添加共价键

    trait Bar[A[+_]] extends Foo[A]
    

    因为 食品 期望协变类型作为参数(+是替换类型约束的一部分),继承类型需要保证参数是协变的(替换类型的限制)。

    在示例1中,您定义 食品 (像容器)是由参数协变的,继承的容器可以是不变的(对替换类型没有限制)

    Martin Lf型理论中的更多细节( 预测参数多态性 )