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?即如果 U
是 Vector
、foo
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 = {
...
}
}
我正在尝试将 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?即如果 U
是 Vector
、foo
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 = {
...
}
}