Почему массивы ковариантны, а обобщения инвариантны?
от эффективного Java Джошуа Блоха,
- массивы отличаются от универсального типа двумя важными способами. Первые массивы являются ковариантными. Дженерики являются инвариантными.
Ковариант просто означает, что если X является подтипом Y, то X[] также будет подтипом Y[]. Массивы ковариантны, так как строка является подтипом объекта So
String[] is subtype of Object[]
инвариант просто означает, независимо от того, является ли X подтипом Y или нет ,
List<X> will not be subType of List<Y>.
мой вопрос в том, почему решение сделать массивы ковариантными в Java? Есть и другие сообщения SO, например, почему массивы инвариантны, но списки ковариантны? но они, похоже, сосредоточены на скале, и я не могу следовать.
1 ответ:
через Википедия:
ранние версии Java и C# не включать дженериков (а.к.а. параметрический полиморфизм).
в такой настройке инвариантность массивов исключает полезные полиморфные программы. Например, можно написать функцию для перетасовки массива или функцию, которая проверяет два массива на равенство с помощью
Object.equals
метод на элементы. Реализация не зависит от конкретного типа элемента, хранящегося в массив, поэтому должна быть возможность написать одну функцию, которая работает на всех типах массивов. Легко реализовать функции типаboolean equalArrays (Object[] a1, Object[] a2); void shuffleArray(Object[] a);
однако, если бы типы массивов рассматривались как инвариантные, можно было бы вызывать эти функции только на массиве точно такого типа
Object[]
. Например, нельзя перетасовать массив строк.поэтому и Java, и C# обрабатывают типы массивов ковариантно. Например, в C#
string[]
является подтипом изobject[]
, и в JavaString[]
является подтипомObject[]
.это отвечает на вопрос " почему массивы ковариантны?", или, точнее, "почему были массивы сделаны ковариантными во время?"
когда дженерики были введены, они целенаправленно не были сделаны ковариантными по причинам, указанным в это ответ Джона Скита:
нет, a
List<Dog>
- это неList<Animal>
. Подумайте, что вы можете делай с собойList<Animal>
- вы можете добавить любое животное к нему... включая кошку. Теперь, можете ли вы логически добавить кошку в помет щенков? Абсолютно нет.// Illegal code - because otherwise life would be Bad List<Dog> dogs = new List<Dog>(); List<Animal> animals = dogs; // Awooga awooga animals.add(new Cat()); Dog dog = dogs.get(0); // This should be safe, right?
вдруг у вас есть очень запутали кошку.
первоначальная мотивация для создания ковариантных массивов, описанная в статье Википедии, не применялась к дженерикам, потому что шаблоны сделал возможным выражение ковариации( и контравариантности), ибо пример:
boolean equalLists(List<?> l1, List<?> l2); void shuffleList(List<?> l);
Comments