Введение в общие типы в Java: ковариантность и контравариантность

1656533412 vvedenie v obshhie tipy v java kovariantnost i kontravariantnost

от Фабиана Терха

0*h03xxe8xFKcFv262

Типы

Java — это статически типизированный язык, означающий, что вы должны сначала объявить переменную и ее тип перед ее использованием.

Например: int myInteger = 42;

Введите общие типы.

Универсальные типы

Определение: «А родовой тип это общий класс или интерфейс, который параметризирован над типами».

По сути, общие типы позволяют написать общий общий класс (или метод), работающий с разными типами, позволяющий повторно использовать код.

Вместо уточнения obj быть с int тип, или a String тип или любой другой тип, который вы определяете Box class, чтобы принять параметр типа <;T>. Тогда вы блn используйте T, чтобы представить этот общий тип в любой части вашего класса.

Теперь введите ковариацию и контравариацию.

Ковариантность и контравариантность

Определение

Дисперсия относится к тому, как подтипирование между более сложными типами связано с подтипированием между их компонентами (источник).

Легкое для запоминания (и чрезвычайно неформальное) определение ковариации и контравариации:

  • Ковариация: принять подтипы
  • Контравариантность: принимать супертипы

Массивы

на Java, массивы ковариантныечто имеет 2 последствия.

Во-первых, массив типов T[] может содержать элементы типа T и их подтипы.

Number[] nums = new Number[5];nums[0] = new Integer(1); // Oknums[1] = new Double(2.0); // Ok

Во-вторых, массив типов S[] является подтипом T[] если S является подтипом T.

Integer[] intArr = new Integer[5];Number[] numArr = intArr; // Ok

Однако важно помнить, что: (1) numArr является ссылкой типа ссылки Number[] к «настоящему объекту» intArr «фактического типа» Integer[].

Таким образом, следующая строка будет скомпилирована отлично, но создаст среду выполнения ArrayStoreException (из-за загрязнения кучи):

numArr[0] = 1.23; // Not ok

Он создает исключение при выполнении, поскольку Java знает при выполнении, что «фактический объект» intArr на самом деле является массивом Integer.

генерики

С общими типами Java нет возможности узнать при выполнении информации о типе параметров типа из-за стирания типа. Поэтому он не может защитить от загрязнения кучи во время выполнения.

Таким образом, генерики являются инвариантными.

ArrayList<Integer> intArrList = new ArrayList<>();ArrayList<Number> numArrList = intArrList; // Not okArrayList<Integer> anotherIntArrList = intArrList; // Ok

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

Но введите символы подстановки.

Подстановочные знаки, ковариация и контравариация

С подстановочными знаками генерики могут поддерживать ковариацию и контравариантность.

Настроив предыдущий пример, мы получаем работающее!

ArrayList<Integer> intArrList = new ArrayList<>();ArrayList<? super Integer> numArrList = intArrList; // Ok

Вопросительный знак «?» ссылается на символ подстановки, представляющий неизвестный тип. Он может быть ограничен снизу, что ограничивает неизвестный тип определенным типом или его супертипом.

Следовательно, в строке 2, ? super Integer переводится как «любой тип, являющийся типом Integer или его супертипом».

Вы также можете ограничить верхний знак подстановки, ограничивающий неизвестный тип определенным типом или его подтипом, используя ? extends Integer.

Только чтение и только запись

Ковариация и контравариантность приносят некоторые интересные результаты. Ковариантные типы доступны только для чтения, а контравариантные – только для записи.

Помните, что типы ковариантов принимают подтипы, следовательно ArrayList<? extends Number> может содержать любой объект of a Тип числа или его подтип.

В этом примере строка 9 работает, потому что мы можем быть уверены, что все, что мы получаем из ArrayList, может быть передано в Number тип (потому что если он расширяется Numberпо определению, это это Number).

Но nums.add() не работает, потому что мы не можем быть уверены в «фактическом типе» объекта. Все, что мы знаем, это то, что это должно быть a Number или его подтипы (например, Integer, Double, Long и т.п.).

С контравариантностью верно наоборот.

Строка 9 работает, потому что мы можем быть уверены, что каким бы «фактическим типом» объекта ни был, он должен быть Integer или его супертип, и таким образом принять an Integer объект.

Но строчка 10 не работает, потому что мы не можем быть уверены, что получим Integer. Например, nums может ссылаться на ArrayList Objects.

Приложения

Поэтому, поскольку ковариантные типы доступны только для чтения, а контравариантные – только для записи (свободно говоря), мы можем вывести следующее правило: «Производитель расширяется, потребитель супер».

Объект, похожий на производителя, создающий объекты типа T может иметь параметр типа <? extends T>, в то время как потребительский объект, потребляющий объекты тип T может быть типа parameter <? супер T>.

Добавить комментарий

Ваш адрес email не будет опубликован.