问题描述
我正在尝试创建一个方法,该方法可以采用 immutable.Seq
的任何后代的类型参数。这是我目前得到的:
def writeSomeData[Holder[_] <: Seq[String]](path: String,holder: Holder[String]): Unit = {
// irrelevant implementation
}
但是,当我使用 immutable.List
调用上述方法时:
writeSomeData(tmp,List(res1,res2,res3,res4,res5))
它因以下错误而中断:
[error] /home/v.gorcinschi/repos/...: inferred type arguments [List] do not conform to method writeSomeData's type parameter bounds [Holder[_] <: Seq[String]]
[error] writeSomeData(tmp,res5))
[error] ^
[error] /home/v.gorcinschi/repos/...: type mismatch;
[error] found : List[String]
[error] required: Holder[String]
[error] writeSomeData(tmp,res5))
为什么会发生这种情况,我该如何纠正? List
是 Seq 的后代,不是吗?
解决方法
问题在于 Holder[_] <: Seq[String]
并不代表您认为的那样。我实际上不确定它是什么意思,可能类似于:trait Holder[A] extends Seq[String]
,正如您所看到的,它与 List 的外观非常不同。
您实际上想表达 Holder[String]
应该是 Seq[String]
的子类型,许多人认为正确的语法是 Holder[_] <: Seq[_]
但是,这也是不正确的;这只是意味着 Holder 必须扩展 Seq 但并不保证 Holder[String] <: Seq[String]
值得庆幸的是,Scala 确实提供了语法来表示: Holder[x] <: Seq[x]
x
不是另一个类型参数,只是表示 Holder[x]
是Seq[x]
用于任何类型 x
有时,您还可以更轻松地使用通用类型约束,例如:
def foo[Holder[_]](holder: Holder[String])(implicit ev: Holder[String] <:< Seq[String]): Unit
表示完全相同的关系。
尽管如此,这仅在您在返回类型中引用 Holder 时才有用,如果您只想使用任何 Seq 那么您可以这样做:
def foo(holder: Seq[String]): Unit
感谢 Liskov,您可以在那里传递 Seq[String]
的任何子类型。
类型约束
Holder[_] <: Seq[String]
means 方法采用任何类型构造函数 Holder
,使得 Holder[X]
是任意 Seq[String]
的 X
的子类型。任意我们的意思是X >: Nothing <: Any
。
现在,当您将 List("")
作为参数传递时,Scala 将尝试统一
?Holder := List
使得 Holder[X] <: Seq[String]
为任意 X
。然而,对于任意 List[X]
,Seq[String]
不是 X
的子类型,例如 X := Int
。因此它不进行类型检查。
作为一种解决方法,我们可以做
Holder[x <: String] <: Seq[x]
这有效地实现了同样的事情,但这次它起作用了,因为现在
Holder[x] <: Seq[x]
确实适用于任意 x
,因为 List
和 Seq
的定义相似
trait Seq[+A]
trait List[+A]
确实 [X] =>> List[X]
<:>[X] =>> Seq[X] 对于任意 x
。
Plus 编译器有来自 x <: String
的额外信息 Holder[x <: String]
来检查参数的元素类型。
给定
Holder[_] <: Seq[String]
您可以通过帮助编译器显式传递适当类型的 lambda 来使其工作
writeSomeData[[X] =>> List[String]](???,List(""))
但是我认为 Scala 不会自动推断出这种类型的 lambda,因为通常类型构造函数统一是 undecidable。