de Fabian Terh

O introducere la tipurile generice în Java: covarianța și contravarianța

O introducere la tipurile generice in Java covarianta si contravarianta

Tipuri

Java este un limbaj tipizat static, ceea ce înseamnă că trebuie să declarați mai întâi o variabilă și tipul acesteia înainte de a o utiliza.

De exemplu: int myInteger = 42;

Introduceți tipurile generice.

Tipuri generice

Definiție: “A tip generic este o clasă sau o interfață generică care este parametrizată pe tipuri. ”

În esență, tipurile generice vă permit să scrieți o clasă generală (sau metodă) generică care funcționează cu diferite tipuri, permițând reutilizarea codului.

Mai degrabă decât să specifici obj a fi dintr-un int tip sau a String sau orice alt tip, definiți Box pentru a accepta un parametru de tip <; T>. Apoi, tu can utilizați T pentru a reprezenta acel tip generic în orice parte din clasa dvs.

Acum, introduceți covarianța și contravarianța.

Covarianța și contravarianța

Definiție

Varianța se referă la modul în care subtiparea dintre tipurile mai complexe se referă la subtiparea dintre componentele lor (sursă).

O definiție ușor de reținut (și extrem de informală) a covarianței și contravarianței este:

  • Covarianță: acceptați subtipuri
  • Contravarianța: acceptați supertipurile

Matrice

În Java, matricile sunt covariante, care are 2 implicații.

În primul rând, o matrice de tip T[] poate conține elemente de tip T și subtipurile sale.

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

În al doilea rând, o matrice de tip S[] este un subtip de T[] dacă S este un subtip de T.

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

Cu toate acestea, este important să ne amintim că: (1) numArr este o referință a tipului de referință Number[] la „obiectul real” intArr de „tip actual” Integer[].

Prin urmare, următoarea linie se va compila foarte bine, dar va produce un timp de execuție ArrayStoreException (din cauza poluării grămezilor):

numArr[0] = 1.23; // Not ok

Produce o excepție de runtime, deoarece Java știe la runtime că „obiectul real” intArr este de fapt o serie de Integer.

Generice

Cu tipurile generice, Java nu are nicio modalitate de a cunoaște în timpul rulării informațiile despre tipul parametrilor de tip, din cauza ștergerii tipului. Prin urmare, nu poate proteja împotriva poluării grămezilor în timpul rulării.

Ca atare, genericele sunt invariante.

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

Parametrii de tip trebuie să se potrivească exact, pentru a proteja împotriva poluării grămezilor.

Dar introduceți metacaractere.

Comercii, covarianță și contravarianță

Cu metacaracterele, este posibil ca genericele să susțină covarianța și contravarianța.

Modificând exemplul anterior, obținem acest lucru, care funcționează!

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

Semnul de întrebare „?” se referă la un wildcard care reprezintă un tip necunoscut. Poate fi limitat, ceea ce restricționează tipul necunoscut să fie un tip specific sau supertipul său.

Prin urmare, în linia 2, ? super Integer se traduce prin „orice tip care este un tip întreg sau supertipul său”.

Puteți, de asemenea, să activați caracterul wildcard, care restricționează tipul necunoscut să fie un anumit tip sau subtipul acestuia, utilizând ? extends Integer.

Numai în citire și numai în scriere

Covarianța și contravarianța produc unele rezultate interesante. Tipurile Covariante sunt numai în citire, în timp ce tipurile contravariante sunt numai în scriere.

Amintiți-vă că tipurile covariante acceptă subtipuri, deci ArrayList<? extends Number> poate conține orice obiect care este fie of a Tipul de număr sau subtipul acestuia.

În acest exemplu, linia 9 funcționează, deoarece putem fi siguri că orice obținem din ArrayList poate fi transformat într-un Number tip (pentru că dacă se extinde Number, prin definiție, it este o Number).

Dar nums.add() nu funcționează, deoarece nu putem fi siguri de „tipul real” al obiectului. Tot ce știm este că trebuie să fie un Number sau subtipurile sale (de ex. Întreg, Dublu, Lung etc.).

Cu contravarianța, inversul este adevărat.

Linia 9 funcționează, deoarece putem fi siguri că oricare ar fi „tipul real” al obiectului, trebuie să fie Integer sau supertipul său și astfel acceptați un Integer obiect.

Dar linia 10 nu funcționează, deoarece nu putem fi siguri că vom obține un Integer. De exemplu, nums ar putea face referire la un ArrayList de Objects.

Aplicații

Prin urmare, din moment ce tipurile covariante sunt numai în citire și tipurile contravariante sunt doar în scriere (vorbind liber), putem obține următoarea regulă generală: „Producătorul extinde, super consumatorul”.

Un obiect de tip producător care produce obiecte de tip T poate fi de tip parametru <? extends T>, în timp ce un obiect de tip consumator care consumă obiecte de tipul T poate fi de tip parameter <? super T>.