2016-08-19 8 views
3

Почему компилятор рассматривает t1 ++ t2 как List [Any]? Объединение двух списков типа S должно возвращать только список типов S.Параметры подтипа Scala generic

// compiles 
def cons[S <: List[Any]](t1: S, t2: S): S = t1 

// type mismatch; found List[Any] required S 
def cons[S <: List[Any]](t1: S, t2: S): S = t1 ++ t2 

ответ

3

Вот что, я считаю, происходит. Прежде всего, 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.

+0

В вашем третьем примере вы говорите: «Здесь и t1 и t2 имеют точно такой же тип, независимо от того, что S». Как это может быть? Список [Int] List [Double] имеют разные типы arent? – Samar

+0

Я думаю, что путаница возникает из-за того, что различные типы могут быть первоначально переданы в качестве аргументов, но тип для S будет ближайшим общим предком различных переданных типов. – Samar

+0

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

1

List[Any] ++ List[Any] является List[Any] не гарантирует S, подтип List[Any] также имеют свойство S ++ S является S, поэтому компилятор возвращается к List[Any].

+0

Я думаю, что ответ Виктора действительно разъясняет, что происходит. Я еще не уверен на 100%, но я думаю, что когда два фактических аргумента t1/t2 передаются методу cons, типы переданных в аргументах прослеживаются назад к их ближайшему общему предку, и это тип S. Not переданный в виде аргумента. – Samar

 Смежные вопросы

  • Нет связанных вопросов^_^