【问题标题】::: method in List - contravariant argument type:: List 中的方法 - 逆变参数类型
【发布时间】:2013-02-15 11:57:29
【问题描述】:

一个简单的问题。类不可变List的方法::定义为:

密封抽象类List[+A] ...

def ::[B >: A](x: B): 列表[B]

假设我有:

class Fruit
class Mango extends Fruit
scala> val d:List[Fruit] = List.empty[Fruit]
d: List[Fruit] = List()

scala> new Mango :: d
res5: List[Fruit] = List(Mango@272d6774)

现在我在这里感到困惑。根据:: 声明,参数类型应该是逆变的。即在这种情况下,>: Fruit 的任何类(我理解为什么会这样)。但是我不明白的是,Mango <: Fruit,那为什么编译器不抛出错误呢?

【问题讨论】:

    标签: scala generics


    【解决方案1】:

    即使List 不变,您仍然可以将Mango 添加到d,因为Mango 仍然是Fruit。用一个普通的旧 java 列表来考虑它:

    val d = new java.util.ArrayList[Fruit]
    d.add(new Mango)
    

    因此,逆变不会限制您添加子类型的能力 - 它使您能够添加超类型并从中获取经过编译器检查的列表类型:

    val d: List[Fruit] = Nil
    val a: List[Plant] = new Carrot :: d
    

    【讨论】:

    • 同意。那是List 的属性。但是这里的方法限制了参数的类型。它应该是_ >: Fruit,但它的行为类似于_ <: Fruit
    • 不,它的行为不像_ <: Fruit。如果是这样,new Object :: d 将不起作用(实际上确实如此)。
    • 我试着改写一下:该方法需要一个B 类型的参数,它是A非严格 超类型,即B >: A。结果列表将具有最具体的常见超类型。例如。当A = FruitB = Mango 你最终得到一个List[Fruit];当A = FruitB = Plant 你得到一个List[Plant]。总结一下:该方法旨在让您构建更通用的类型容器,而不是限制您添加特定的子类型(顺便说一句,根据 Liskov,这是不可能的)
    【解决方案2】:

    排队

    new Mango :: d
    

    您希望编译器推理:“new Mango 的类型为 Mangod 的类型为 List[Fruit],因此 List[Fruit].::(Mango) 需要进行类型检查,但它不需要”。在这种情况下,它确实是一个错误。

    但实际上,它的原因不同:“我需要new Mango :: d 进行类型检查,所以new Mango 必须有一些类型B 这样B :> Fruit。有没有这样的@987654331 @?是的,B = Fruit。”所以没有错误。

    【讨论】:

      猜你喜欢
      • 2014-04-30
      • 1970-01-01
      • 2015-10-18
      • 1970-01-01
      • 1970-01-01
      • 2017-05-13
      • 2012-04-07
      • 1970-01-01
      • 2022-08-05
      相关资源
      最近更新 更多