Uvod u generičke tipove u Javi: kovarijancija i kontravarijancija

Vrste

Java je statički otkucan jezik, što znači da prije upotrebe morate deklarirati varijablu i njen tip.

Na primjer: int myInteger = 42;

Unesite generičke vrste.

Generički tipovi

Definicija: " Generički tip je generička klasa ili sučelje koje se parametrizira u odnosu na tipove."

U osnovi, generički tipovi omogućuju vam pisanje opće, generičke klase (ili metode) koja radi s različitim vrstama, što omogućuje ponovnu upotrebu koda.

Umjesto da odredite objda je inttipa, Stringtipa ili bilo kojeg drugog tipa, definirate Boxklasu koja prihvaća parametar tipa <; T>. Zatim možete nkoristiti T za predstavljanje tog generičkog tipa u bilo kojem dijelu vaše klase.

Sada unesite kovarijanciju i kontravarijanciju.

Kovarijancija i kontravarijancija

Definicija

Varijansa se odnosi na to kako se podtipiranje složenijih tipova odnosi na podtipiranje njihovih komponenata (izvor).

Jednostavna (i krajnje neformalna) definicija kovarijancije i kontravarijance je:

  • Kovarijancija: prihvaća podtipove
  • Kontravarijansa: prihvatite supertipove

Nizovi

U Javi su nizovi kovarijantni , što ima dvije implikacije.

Prvo, niz vrsta T[]može sadržavati elemente tipa Ti njegove podtipove.

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

Drugo, niz vrsta S[]je podvrsta T[]if Sje podtip tipa T.

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

Međutim, važno je imati na umu da je: (1) numArrreferenca referentnog tipa Number[]na "stvarni objekt" intArr"stvarnog tipa" Integer[].

Stoga će se sljedeći redak izvrsno kompilirati, ali će stvoriti vrijeme izvođenja ArrayStoreException(zbog onečišćenja gomile):

numArr[0] = 1.23; // Not ok

Proizvodi runtime iznimku, jer Java tijekom izvođenja zna da je "stvarni objekt" intArrzapravo niz Integer.

Generički lijekovi

Kod generičkih tipova Java nikako ne može tijekom izvođenja znati podatke o tipu parametara tipa zbog brisanja tipa. Stoga ne može zaštititi od onečišćenja hrpom tijekom izvođenja.

Kao takvi, generički lijekovi su invarijantni.

ArrayList intArrList = new ArrayList();ArrayList numArrList = intArrList; // Not okArrayList anotherIntArrList = intArrList; // Ok

Parametri tipa moraju se točno podudarati kako bi se zaštitili od onečišćenja gomile.

Ali unesite zamjenske znakove.

Zamjenski znakovi, kovarijancija i kontravarijancija

Uz zamjenske znakove moguće je da generički lijekovi podržavaju kovarijanciju i kontravarijanciju.

Ugađajući prethodni primjer, dobivamo ovo, što djeluje!

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

Znak pitanja "?" odnosi se na zamjenski znak koji predstavlja nepoznati tip. Može biti niže ograničena, što ograničava nepoznati tip da bude određeni tip ili njegov nadtip.

Stoga se u retku 2 ? super Integerprevodi u "bilo koji tip koji je Integer tip ili njegov supertip".

Možete i gornjom granicom zamjenskog znaka, koji ograničava nepoznati tip da bude određeni tip ili njegov podtip, pomoću ? extends Integer.

Samo za čitanje i samo za pisanje

Kovarijancija i kontravarijacija daju neke zanimljive ishode. Kovarijantni tipovi su samo za čitanje, dok su kontravarijantni tipovi samo za čitanje.

Zapamtite da kovarijantni tipovi prihvaćaju podtipove, pa ArrayList er> 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>.