Вступ до загальних типів у Java: коваріація та контраваріація

Типи

Java - це статично набрана мова, що означає, що перед використанням потрібно спочатку оголосити змінну та її тип.

Наприклад: int myInteger = 42;

Введіть загальні типи.

Родові типи

Визначення: " Загальний тип - це загальний клас або інтерфейс, який параметризований для типів".

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

Замість того , щоб вказати obj, що з intтипу, або Stringтипу, або будь-якого іншого типу, ви визначаєте Boxклас , щоб прийняти параметр типу <, 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[]if 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 intArrList = new ArrayList();ArrayList numArrList = intArrList; // Not okArrayList anotherIntArrList = intArrList; // Ok

Параметри типу повинні точно відповідати, щоб захистити від кучевого забруднення.

Але введіть символи підстановки.

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

За допомогою символів узагальнення генерики можуть підтримувати коваріацію та контраваріацію.

Налаштувавши попередній приклад, ми отримуємо це, що працює!

ArrayList intArrList = new ArrayList();ArrayList numArrList = intArrList; // Ok

Знак питання "?" відноситься до символу підстановки, який представляє невідомий тип. Він може бути нижчим, що обмежує невідомий тип певним типом або його супертипом.

Отже, у рядку 2 ? super Integerперекладається як "будь-який тип, який є цілим числом або його супертипом".

Ви також можете встановити верхню межу шаблону, який обмежує невідомий тип певним типом або його підтипом, використовуючи ? extends Integer.

Лише для читання та лише для запису

Коваріація та контраваріація дають цікаві результати. Коваріантні типи доступні лише для читання, тоді як противаріантні типи - лише для читання.

Пам'ятайте, що коваріантні типи приймають підтипи, отже ArrayLister> can contain any object that is either of a Number type or its subtype.

In this example, line 9 works, because we can be certain that whatever we get from the ArrayList can be upcasted to a Number type (because if it extends Number, by definition, it is a Number).

But nums.add() doesn’t work, because we cannot be sure of the “actual type” of the object. All we know is that it must be a Number or its subtypes (e.g. Integer, Double, Long, etc.).

With contravariance, the converse is true.

Line 9 works, because we can be certain that whatever the “actual type” of the object is, it must be Integer or its supertype, and thus accept an Integer object.

But line 10 doesn’t work, because we cannot be sure that we will get an Integer. For instance, nums could be referencing an ArrayList of Objects.

Applications

Therefore, since covariant types are read-only and contravariant types are write-only (loosely speaking), we can derive the following rule of thumb: “Producer extends, consumer super”.

A producer-like object that produces objects of type T can be of type parameter T>, while a consumer-like object that consumes objects oftype T can be of type parameter super T>.

Original text