协方差和方法参数

时间:2014-04-23 16:22:51

标签: scala covariance contravariance

我的项目中有一个令人困惑的问题并且无法解决,所以请帮助我!

以下示例代码简化了原始代码:

trait Sample[A] {
    def doit(param: A)
}

case object SampleEx1 extends Sample[Int] {
    def doit(param: Int) = {
        param + 0
    }
}

现在我需要根据外部原因制作A协方差,但是会导致错误,如下所示:

trait Sample[+A] {
    def doit(param: A) // ERR: covariant type A occurs in contravariant position in type A of value param
}

case object SampleEx1 extends Sample[Int] {
    def doit(param: Int) = {
        param + 0
    }
}

所以我stacoverflow并找到了另一种类型B的解决方案,但随后发生了另一个错误:

trait Sample[+A] {
    def doit[B >: A](param: B)
}

case object SampleEx1 extends Sample[Int] {
    def doit[Int](param: Int) = {
        param + 0 // type mismatch; found : Int(0) required: String
    }
}

由于param,显然Int不再是[B >: Int]

我尝试用自己和谷歌来解决这个问题,但无法得到它。有人可以帮忙吗?非常感谢! :))

1 个答案:

答案 0 :(得分:5)

第一个错误covariant type A occurs in contravariant position in type A of value param表示如果泛型类型Foo声明自己在T(即Foo[+T])上是协变的,则意味着它的方法只能返回T而不要求它。否则将违反类型一致性。例如,您可以传入Sample[Dog]的实例,其中Sample[Animal]是必需的,然后可以在其上调用doit(new Duck),即使Sample[Dog]#doit只能处理Dog的实例1}}。但是,在这种情况下,返回值的行为完全相反(我会让你找出原因)。

但是这个

def doit[Int](param: Int)

表示doit有一个名为Int的类型参数,它与Int类型无关(虽然它看起来确实像第一印象一样,这就是为什么你永远不要使用与其他/内置类型的名称一致的类型参数名称。因此,您获得的错误是因为该上下文中的Int表示"任何类型",并且在任何类型上使用+将回退到字符串连接而不是算术此外。

而你需要(正确地从Sample[+A]继承):

def doit[B >: Int](param: B)

但是,仍然不允许您在param上添加,因为param现在是Int的任何超类型,而不是Int本身或其子类型。< / p>

所以我不知道你怎么能&#34;修复&#34;这种 - 方差的工作方式基本上不允许泛型类型在方法参数上协变。这与Scala无关。但是请看例如http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/http://docs.scala-lang.org/tutorials/tour/variances.html有关方差如何工作以及为什么必须与其完全相同(在任何语言中实施正确的方差规则)的更多信息。

我认为在Stackoverflow上获得一个非常有用的答案的一般方法是描述你真正需要实现的目标,而不仅仅是你到目前为止所做的实现。