我正在尝试将y转换为可以附加到x的东西,其中x是某种排序的序列。
scala> def foo[U <: Seq[T], T](x: U, y: T): U = x :+ y
<console>:7: error: type mismatch;
found : Seq[T]
required: U
def foo[U <: Seq[T], T](x: U, y: T): U = x :+ y
^
我有以下解决方案:
def foo[T]( x : Seq[T], y:T) = x :+ y
def foo[T]( x : Seq[T], y:T) : Seq[T] = x :+ y
def foo[U <: Seq[T], T](x: U, y: T): U = (x :+ y).asInstanceOf[U]
但我的疑问是为什么原来的那个不起作用。它看起来像如果我应用一个操作符(:+
在这种情况下)定义在超类然后它返回超类?即,如果U
是Vector
, foo
返回Seq
,所以我得到错误required "U" but found "Seq[T]"
。
谁能告诉我为什么会出现这种行为?
在遇到类型问题时,我通常采用"如果它通过编译,会发生什么"的逻辑来寻找不合理的部分
在你的情况下,假设原来的是好的。
def foo[U <: Seq[T], T](x: U, y: T): U = x :+ y
导致Seq[T]在T上协变,所以下面的情况成立。
for type A, T, if A <: T, List[A] <: Seq[T]
然后我们可以做以下操作:
class Parent
class Child extends Parent
// List(new Child) :+ (new Parent) => List[Parent]
val result = foo(List(new Child), new Parent)
U实际上是foo方法中的List[Child],但是当List操作与其元素类型不同的类型时,它将尝试找到共同的父元素,在这种情况下,结果类型为List[parent],但所需的类型是List[Child]。显然,List[Parent]不是List[Child]的子类型。
问题是最终类型被提升了,但所需的类型是提升类型的子类型。如果你看一下Scala SeqLike的定义,这可能会更清楚。
trait SeqLike[+A, +Repr] extends ... {
def :+[B >: A, That](elem: B)(...): That = {
...
}
}
让我们简化这个例子
class T
class B extends T
def bar[U <: T](x: T): U = {
new B
}
这不会编译,因为当你调用
bar(new T)
你应该返回类型T,但你试图返回类型B。B是T的子类型,但你应该确切地返回U,而不仅仅是T的子类型。
你可以通过
修复你的问题def foo[U <: Seq[T], T](x: U, y: T): Seq[T] = x :+ y
或
def foo[B >: Seq[T], U <: Seq[T], T](x: U, y: T): B = y +: x