考虑这个简短的片段:
trait Table[+A] {
type RowType = Seq[A]
}
Scala 2.11.7编译器出现以下错误:
covariant type A occurs in invariant position in type Seq[A] of type RowType
为什么A
被认为在Seq[A]
中处于不变位置而Seq
本身被定义为trait Seq[+A]
?
另外,如果我们忽略错误,您能否提供一个用例来说明此类型定义可能存在的问题?
答案 0 :(得分:8)
对于任何B <: A
,您的Table[B]#RowType
将比Table[A]#RowType
更具体。更具体的含义并不相同,因此编译器将类型别名的参数视为不变位置。
你如何解决这个问题。
你可以将你的类型定义为抽象,这意味着你应该在以后定义它并且可能遇到同样的问题,但是在trait Table
级别这样的定义是正确的
trait Table[+A] {
type RowType <: Seq[A]
}
您可以定义参数化类型成员,这可能会导致您更改使用此类型的方式,但在大多数情况下应该执行此任务。
trait Table[+A] {
type RowType[+X] = Seq[X]
}
不是我最强的领域,但我试着描述我的想法。
假设你有
trait Table[+A] {
type RowType = Seq[A]
}
def mkTable[A]: Table[A] = new Table[A] {}
然后你做了以下
val tupleTable = mkTable[(String, String)]
val prodTable: Table[Product] = tupleTable
那么prodTable.RowType
会是什么?
如果您的定义应该是Seq[Product]
。但等等,prodTable
和tupleTable
是同一个对象,因此其成员应该相同,因此prodTable.RowType
应为Seq[(String, String)]
但是如果你改成第一种方法,比如
trait Table[+A] {
type RowType <: Seq[A]
}
def mkTable[A]: Table[A] = new Table[A] {
type RowType = Seq[A]
}
编译器会知道RowType
的{{1}}属于某种类型Table[Product]
,<: Seq[Product]
属实,所有歧义都消失了。
答案 1 :(得分:2)
这是一个反例:
import scala.annotation.unchecked.uncheckedVariance
class Foo[+S](x: S) {
type Shape = S @uncheckedVariance // THIS IS BAD
}
object Test {
def main(args: Array[String]): Unit = {
val fs: Foo[String] = new Foo("")
def cast(x: Foo[Any]): x.Shape = 1 // This typechecks since x.Shape =:= Any
val s: String = cast(fs) // This typechecks because Foo[String] <: Foo[Any], but results in a ClassCastException
}
}
抛出:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Test$.main(main.scala:12)
Scastie 链接:https://scastie.scala-lang.org/smarter/Cno5sLZyTSybDC80uDt2hQ。归功于@smarter。