Вот что, я считаю, происходит. Прежде всего, S
такой же тип везде, здесь нет магии. Давайте посмотрим на первый примере:
scala> def cons[S <: List[Any]](t1: S, t2: S): S = if(t1.isEmpty) t1 else t2
cons: [S <: List[Any]](t1: S, t2: S)S
scala> cons(List(1), List(2.0))
res21: List[AnyVal] = List(2.0)
Как вы можете видеть Scala правильно нашел ближайший общий предок для Int
и Double
, и это AnyVal
. Таким образом, в этом случае S
- AnyVal
.
Теперь давайте попробуем это:
scala> def cons[S <: List[Any]](t1: S, t2: S): S = t1 ++ t2
<console>:11: error: type mismatch;
found : List[Any]
required: S
def cons[S <: List[Any]](t1: S, t2: S): S = t1 ++ t2
^
Что случилось? Это сообщение об ошибке означает, что результатом ++
является как-то List[Any]
вместо ожидаемого S
. Почему это? Давайте посмотрим на ++
подписи (упрощенно, реальная подпись длиннее):
def ++[B >: A](other: List[B]): List[B] = ???
Так Scala нужно найти ближайший предок A
и фактический параметр типа в other
. Единственная проблема: она должна найти B
в точке, где вы определяете cons
, а не там, где вы ее применяете позже (B
не является бесплатным параметром для cons
). Единственной информацией является верхняя граница S
, и это List[Any]
, поэтому единственным безопасным решением для B
в точке определения cons
является наиболее общий, то есть Any
. Это означает, что результат ++
равен List[Any]
, и он не подходит S
. Отсюда и ошибка.
Третий пример:
scala> def cons[S <: Any](t1: List[S], t2: List[S]): List[S] = t1 ++ t2
cons: [S](t1: List[S], t2: List[S])List[S]
scala> cons(List(1), List(1.0))
res0: List[AnyVal] = List(1, 1.0)
Почему это работает? Здесь и t1
, и t2
имеют точно такой же тип, независимо от того, что S
is (и S
может быть выведено позже). Таким образом, B == S
и результат List[S]
. Опять же в этом конкретном случае S
является ближайшим общим предком Int
и Double
.
В вашем третьем примере вы говорите: «Здесь и t1 и t2 имеют точно такой же тип, независимо от того, что S». Как это может быть? Список [Int] List [Double] имеют разные типы arent? – Samar
Я думаю, что путаница возникает из-за того, что различные типы могут быть первоначально переданы в качестве аргументов, но тип для S будет ближайшим общим предком различных переданных типов. – Samar
I второй вопрос Самар. Я не вижу разницы между '(t1: S, t2: S)' и '(t1: List [S], t2: List [S])'. В обоих случаях t1 и t2 имеют одинаковый тип. Конечно, вы можете передать «List [Int]» и «List [String]», но в этом случае ближайшим предком является «List [Any]», так что это 'S'. Можете ли вы привести пример для 'def cons [S <: List [Any]] (t1: S, t2: S): S', где бы у нас была ошибка (следовательно, компилятор не позволяет это)? Независимо от того, что вы проходите, компилятор выведет 'S' как ближайшего предка и ** это то, что будет возвращено **. Выведенный тип 'S'. Он должен работать с 'S', тогда нет? – slouc