Обобщения Scala - Почему Scala возвращает экземпляр супертипа вместо подтипа, когда используется ограничение типа?
Я пытаюсь преобразовать 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]"
,
Может ли кто-нибудь просветить меня, почему это поведение замечено?
2 ответа
Когда я сталкиваюсь с проблемами типа, я обычно использую логику "если она пройдет компиляцию, что произойдет", чтобы найти необоснованную часть.
В вашем случае, если исходный вариант - "Хорошо".
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 - это List[Child] в методе foo, но когда List работает с типом, отличным от его типа элемента, он попытается найти общего родителя, в этом случае результат набирается с помощью List[Parent], но необходимого типа. Список [Ребенок]. Очевидно, 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