Scala 泛型 - 为什么在使用类型约束时 scala returns 是超类型的实例而不是子类型?

Scala generics - Why scala returns an instance of supertype instead of the subtype when type constraint is used?

我正在尝试将 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]

但我的疑问是为什么原来的那个不起作用。看起来如果我应用在超级 class 中定义的运算符(在本例中为 :+),那么它会 returns 超级 class?即如果 UVectorfoo returns Seq,那么我得到错误 required "U" but found "Seq[T]".

谁能告诉我为什么会看到这种行为?

让我们简化这个例子

  class T
  class B extends T

  def bar[U <: T](x: T): U = {
    new B
  }

这不会编译,因为当你调用

bar(new T)

你应该 return 键入 T,但你试图 return 键入 B。B 是 T 的子类型,但你应该 return 完全是 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

在遇到类型问题时,我通常采用"if it passes the compilation, what will happen"逻辑来查找不合理的部分。

在你的情况下,假设原来的没问题。

 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 = {
       ...
    }
}