Почему массивы ковариантны, а обобщения инвариантны?



от эффективного Java Джошуа Блоха,




  1. массивы отличаются от универсального типа двумя важными способами. Первые массивы являются ковариантными. Дженерики являются инвариантными.


  2. Ковариант просто означает, что если X является подтипом Y, то X[] также будет подтипом Y[]. Массивы ковариантны, так как строка является подтипом объекта So



    String[] is subtype of Object[]



    инвариант просто означает, независимо от того, является ли X подтипом Y или нет ,



     List<X> will not be subType of List<Y>.



мой вопрос в том, почему решение сделать массивы ковариантными в Java? Есть и другие сообщения SO, например, почему массивы инвариантны, но списки ковариантны? но они, похоже, сосредоточены на скале, и я не могу следовать.

564   1  

1 ответ:

через Википедия:

ранние версии Java и C# не включать дженериков (а.к.а. параметрический полиморфизм).

в такой настройке инвариантность массивов исключает полезные полиморфные программы. Например, можно написать функцию для перетасовки массива или функцию, которая проверяет два массива на равенство с помощью Object.equals метод на элементы. Реализация не зависит от конкретного типа элемента, хранящегося в массив, поэтому должна быть возможность написать одну функцию, которая работает на всех типах массивов. Легко реализовать функции типа

boolean equalArrays (Object[] a1, Object[] a2);
void shuffleArray(Object[] a);

однако, если бы типы массивов рассматривались как инвариантные, можно было бы вызывать эти функции только на массиве точно такого типа Object[]. Например, нельзя перетасовать массив строк.

поэтому и Java, и C# обрабатывают типы массивов ковариантно. Например, в C# string[] является подтипом из object[], и в Java String[] является подтипом 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

    Ничего не найдено.