Подтвердить что ты не робот

О дженериках в Java и Scala

Меня смущает общий подтипирование.

В Java, если тип A является подтипом B, общий тип C<A> и C<B> являются инвариантными. Например, ArrayList<Base> не является подтипом ArrayList<Derived>.

Однако в Scala общий тип C<A> и C<B> являются ковариантными, если тип A является подтипом B. Итак, что свойство универсального класса в Scala имеет, но не в Java?

4b9b3361

Ответ 1

Во-первых, обратите внимание, что дисперсия является свойством параметров типового типа, а не самих параметризованных типов.

Во-вторых: вы ошибаетесь в отношении параметров scala - по умолчанию инвариантны. Исследуем!

Java

Java имеет аннотации вариаций на сайте. То есть вы можете объявить методы следующим образом:

boolean addAll(Collection<? extends T> c);

Существует, однако, одна форма "параметризованного типа" (я использую термин "свободно" ), в котором параметры типа ковариантны: массивы Java! (Это на самом деле безумное, потому что массивы изменяемы и, следовательно, легко обойти систему типов). Рассмотрим следующее:

public static void subvert(Object[] arr) { arr[0] = "Oh Noes!"; }

И затем:

Integer[] arr = new Integer[1];
subvert(arr); //this call is allowed as arrays are covariant
Integer i = arr[0];

Хороший вопрос для интервью: что происходит?

Scala

В Scala у вас есть отклонение объявления-сайта. То есть дисперсия параметра типа объявляется рядом с параметром (используя аннотации + и -):

trait Function1[-I, +O]

Это говорит о том, что признак Function1 имеет два типа параметров, I и O, которые являются соответственно и со-вариантами. Если объявлена ​​аннотация +/-, то параметр типа является инвариантным. Например, Set является инвариантным в своем параметре типа:

scala> def foo(set: Set[Any]) = ()
foo: (set: Set[Any])Unit

scala> Set(1)
res4: scala.collection.immutable.Set[Int] = Set(1)

scala> foo(res4)
<console>:10: error: type mismatch;
 found   : scala.collection.immutable.Set[Int]
 required: Set[Any]
Note: Int <: Any, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
              foo(res4)
                  ^

Список, однако, объявлен как ковариантный:

scala> def bar(list: List[Any]) = ()
bar: (list: List[Any])Unit

scala> List(1)
res6: List[Int] = List(1)

scala> bar(res6)

Почему бы не спросить компилятор?

Другой способ продемонстрировать это - запросить компилятор непосредственно для подтипов:

scala> class Cov[+A]
defined class Cov

scala> implicitly[Cov[Int] <:< Cov[Any]]
res8: <:<[Cov[Int],Cov[Any]] = <function1>

Но с параметром инвариантного типа

scala> class Inv[A]
defined class Inv

scala> implicitly[Inv[Int] <:< Inv[Any]]
<console>:9: error: Cannot prove that Inv[Int] <:< Inv[Any].
              implicitly[Inv[Int] <:< Inv[Any]]
                        ^

Наконец, контравариантность:

scala> class Con[-A]
defined class Con

scala> implicitly[Con[Any] <:< Con[Int]]
res10: <:<[Con[Any],Con[Int]] = <function1>

См. также идентификатор <:<