Проблема с параметризованным регистром класса ограниченного типа и аргументами по умолчанию в Scala - PullRequest
7 голосов
/ 26 мая 2011

Обратите внимание на следующее (протестировано с Scala 2.8.1 и 2.9.0):

trait Animal
class Dog extends Animal

case class AnimalsList[A <: Animal](list:List[A] = List())
case class AnimalsMap[A <: Animal](map:Map[String,A] = Map())

val dogList = AnimalsList[Dog]()  // Compiles
val dogMap = AnimalsMap[Dog]()    // Does not compile

Последняя строка завершается с ошибкой:

error: type mismatch;
 found   : scala.collection.immutable.Map[Nothing,Nothing]
 required: Map[String,Main.Dog]
Note: Nothing <: String, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: String`. (SLS 3.2.10)
Error occurred in an application involving default arguments.
    val dogMap = AnimalsMap[Dog]()    // Does not compile
                       ^
one error found

Изменение на val dogMap = AnimalsMap[Dog](Map()) исправленияэто, но больше не использует преимущество значения аргумента по умолчанию.

Почему значение по умолчанию выводится как Map [Nothing, Nothing], учитывая, что аналог List работает должным образом?Есть ли способ создать экземпляр AnimalsMap, который использует значение по умолчанию для map arg?


Редактировать: Я принял ответ на свою более неотложную секундувопрос, но мне все равно было бы интересно узнать, почему тип ключа для Map() выводится по-разному между этими двумя случаями:

case class AnimalsMap1(map:Map[String,Animal] = Map())
val dogs1 = AnimalsMap1() // Compiles

case class AnimalsMap2[A <: Animal](map:Map[String,A] = Map())
val dogs2 = AnimalsMap2[Dog]() // Does not compile

Edit 2: Кажется, чтограницы типов не имеют значения - любой параметрический тип для класса case вызывает проблему:

case class Map3[A](map:Map[String,A] = Map())
val dogs3 = Map3[Dog]() // Does not compile

Ответы [ 2 ]

20 голосов
/ 26 мая 2011

В Scala есть функция, позволяющая определить класс, который будет ковариантным / контравариантным в его общих параметрах.

В качестве примера ковариации: естественно думать, что если class Student extends Person, то List[Student] "расширяет" List[Person]. Это связано с тем, что у каждого метода, принимающего List[Person], не должно возникнуть проблем при работе с объектом List[Student]. Это невозможно в Java (не делая метод также общим).

Contravariance противоположен и немного сложнее объяснить. Требуется, когда тип должен быть передан в общий класс, а не прочитан (в List[Person] вы читаете элементы списка). Общий пример - это функция. В него помещаются типы аргументов функции, поэтому, если метод ожидает функцию Person => String, он не может быть вызван с функцией Student => String (он вызовет аргумент с человеком, но ожидает студента )

Scala также определяет Nothing, чтобы неявно расширять все. Это нижний тип. Так что List[Nothing] всегда «расширяет» List[X] для любого X. List() создает List[Nothing], и ковариация - вот почему вы можете написать val x: List[Person] = List().

В любом случае, Карта инвариантна в своих типах ключей. Причина в том, что Map[A, B] подобна функции A => B, поэтому она может быть только контравариантной в A. Другой способ - подумать, что произойдет, если вы передадите Map[Student, String] методу, ожидающему Map[Person, String], очевидно, он может попытаться поместить туда объекты Person, что нехорошо, другой способ в порядке. С другой стороны, карту можно рассматривать как Iterable[(A, B)], здесь она должна быть ковариантной в A. Таким образом, она инвариантна по своему значению.

В результате вы не можете присвоить Map[Nothing, Nothing] переменной типа Map[String, Animal]. Map() создает Map[Nothing, Nothing]

Компилятор скажет вам следующее:

scala> val dogs3 = Map3[Dog]()
<console>:13: error: type mismatch;
 found   : scala.collection.immutable.Map[Nothing,Nothing]
 required: Map[String,Dog]
Note: Nothing <: String, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: String`. (SLS 3.2.10)
Error occurred in an application involving default arguments.
       val dogs3 = Map3[Dog]()
                       ^
4 голосов
/ 26 мая 2011

Просто помогите компилятору:

case class AnimalsMap[A <: Animal](map:Map[String,A] = Map[String, A]())
                                                          ^^^^^^^^^^^

Я оставлю детали того, почему ваше решение не работает, кому-то, кто лучше знаком с выводом типа Scala ...

Редактировать: См. Ответ IttayD для хорошего объяснения этого поведения.

...