scala – 为什么Seq.contains接受类型Any而不是类型参数A?
例如:
scala> val l:List[String] = List("one","two") l: List[String] = List(one,two) scala> l.contains(1) //wish this didn't compile res11: Boolean = false The various explanations of why things were done this way in Java似乎并没有在这里应用,因为Map和Set实现了包含和朋友的类型安全版本.有没有办法做一个类型安全的包含在一个Seq不克隆到一个集合? 解决方法
问题是Seq在其类型参数中是协变的.这对其大部分功能来说是非常有意义的.作为一个不可变的容器,它应该是一致的.不幸的是,当它们必须定义一个采用一些参数化类型的方法时,这样做会妨碍.请考虑以下示例:
trait Seq[+A] { def apply(i: Int): A // perfectly valid def contains(v: A): Boolean // does not compile! } 问题是函数的参数类型总是相反的,在返回类型中是协变的.因此,apply方法可以返回类型A的值,因为A与应用的返回类型是协方差的.但是,contains不能使用类型A的值,因为它的参数必须是逆向的. 这个问题可以用不同的方式解决.一个选择是简单地使A成为一个不变类型的参数.这允许它在协变和逆转上下文中使用.但是,这个设计意味着Seq [String]不会是Seq [Any]的子类型.另一个选项(和最常用的一个)是使用一个局部类型的参数,下面是协方差类型.例如: trait Seq[+A] { def +[B >: A](v: B): Seq[B] } 这个技巧保留Seq [String]< ;: Seq [Any]属性,并在编写使用异构容器的代码时提供一些非常直观的结果.例如: val s: Seq[String] = ... s + 1 // will be of type Seq[Any] 此示例中的函数的结果是Seq [Any]类型的值,因为Any是类型为String和Int(换句话说,最不常见的超类型)的最小上限(LUB).如果你考虑,这正是我们期望的行为.如果您创建一个包含String和Int组件的序列,那么它的类型应该是Seq [Any]. 不幸的是,这个技巧在适用于包含的方法时会产生一些令人惊讶的结果: trait Seq[+A] { def contains[B >: A](v: B): Boolean // compiles just fine } val s: Seq[String] = ... s contains 1 // compiles! 这里的问题是我们调用contains方法传递Int类型的值. Scala看到这一点,并尝试推断一个类型为B,它是Int和A的超类型,在这种情况下,它被实例化为String.这两种类型的LUB是Any(如前所示),因此包含的本地类型实例化将为Any =>布尔.因此,contains方法看起来不是类型安全的. 此结果不是Map或Set的问题,因为它们的参数类型中都不是协方差的: trait Map[K,+V] { def contains(key: K): Boolean // compiles } trait Set[A] { def contains(v: A): Boolean // also compiles } 因此,长篇小说中,协变容器类型的包含方法不能被限制为只能通过函数类型的工作方式(在参数类型中相反)来获取组件类型的值.这并不是Scala的实施或者实施不好的一个限制,它是一个数学事实. 安慰奖是在实践中这不是一个问题.而且,正如其他答案所提到的那样,您可以随时定义自己的隐式转换,如果您真的需要额外的检查,它将添加一个“类型安全”的包含类似方法. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |