Java >> Java tutorial >  >> Tag >> extends

Java Generics – <?> vs <? udvider Objekt>

1. Oversigt

I denne hurtige selvstudie vil vi se lighederne og forskellene mellem og i Java Generics .

Men da dette er et avanceret emne, er det bydende nødvendigt at få en grundlæggende forståelse af emnet, før vi dykker ned i sagens kerne.

2. Baggrund for generiske lægemidler

Generiske stoffer blev introduceret i JDK 5 for at eliminere køretidsfejl og styrke typesikkerheden. Denne ekstra typesikkerhed eliminerer casting i nogle tilfælde og giver programmører mulighed for at skrive generiske algoritmer, som begge kan føre til mere læsbar kode.

For eksempel, før JDK 5, ville vi skulle arbejde med elementerne i en liste ved hjælp af casting. Dette skabte igen en bestemt klasse af runtime-fejl:

List aList = new ArrayList();
aList.add(new Integer(1));
aList.add("a_string");
        
for (int i = 0; i < aList.size(); i++) {
    Integer x = (Integer) aList.get(i);
}

Nu har denne kode to problemer, vi gerne vil løse:

  • Vi har brug for en eksplicit cast for at udtrække værdier fra aList – typen afhænger af variabeltypen til venstre – Heltal i dette tilfælde
  • Vi får en runtime-fejl ved anden iteration, når vi forsøger at caste a_string til et heltal

Generika udfylder rollen for os:

List<Integer> iList = new ArrayList<>();
iList.add(1);
iList.add("a_string"); // compile time error

for (int i = 0; i < iList.size(); i++) {
    int x = iList.get(i);
}

Compileren vil fortælle os, at det ikke er muligt at tilføje a_string til en liste af typen Heltal , hvilket er bedre end at finde ud af det under kørsel.

Desuden er der ikke behov for eksplicit casting, da compileren allerede ved, at iList indeholder heltal s. På grund af magien ved unboxing havde vi ikke engang brug for et heltal type, er dens primitive form nok.

3. Jokertegn i Generics

Et spørgsmålstegn eller jokertegn bruges i generiske artikler til at repræsentere en ukendt type. Det kan have tre former:

  • Ubundne jokertegn :Liste repræsenterer en liste af ukendt type
  • Jokertegn med øvre grænse :Liste repræsenterer en liste over Nummer eller dets undertyper såsom Heltal og Dobbelt
  • Jokertegn med lavere grænse :Liste repræsenterer en liste over heltal eller dens supertyper Nummer og Objekt

Nu, siden Objekt er den iboende supertype af alle typer i Java, ville vi være fristet til at tro, at den også kan repræsentere en ukendt type. Med andre ord List og Liste kunne tjene samme formål. Men det gør de ikke.

Lad os overveje disse to metoder:

public static void printListObject(List<Object> list) {    
    for (Object element : list) {        
        System.out.print(element + " ");    
    }        
}    

public static void printListWildCard(List<?> list) {    
    for (Object element: list) {        
        System.out.print(element + " ");    
    }     
}

Givet en liste over heltal s, sig:

List<Integer> li = Arrays.asList(1, 2, 3);

printListObject(li) vil ikke kompilere, og vi får denne fejl:

The method printListObject(List<Object>) is not applicable for the arguments (List<Integer>)

Hvorimod printListWildCard(li) kompilerer og udsender 1 2 3 til konsollen.

4. og – lighederne

I ovenstående eksempel, hvis vi ændrer metodesignaturen for printListWildCard til:

public static void printListWildCard(List<? extends Object> list)

Det ville fungere på samme måde som printListWildCard(List list) gjorde. Dette skyldes det faktum, at Objekt er en supertype af alle Java-objekter, og stort set alt udvider Objekt . Så en liste af Heltal s bliver også behandlet.

Kort sagt betyder det, at ? og ? udvider Objekt er synonyme i dette eksempel .

Selvom det i de fleste tilfælde ville holde stik, men der er også et par forskelle . Lad os se på dem i næste afsnit.

5. og – forskellen

Reificerbare typer er dem, hvis type ikke slettes på kompileringstidspunktet. Med andre ord vil en ikke-revidérbar types runtime-repræsentation have mindre information end dens kompileringstids-modstykke, fordi noget af det vil blive slettet.

Som hovedregel er parameteriserede typer ikke reificerbare. Det betyder List og Map er ikke reificerbare. Compileren sletter deres type og behandler dem som en Liste og Kort hhv.

Den eneste undtagelse fra denne regel er ubegrænsede jokertegntyper. Dette betyder Liste og Kort er reificerbare .

På den anden side, List er ikke reificerbar . Selvom det er subtilt, er dette en bemærkelsesværdig forskel.

Ikke-tilbageførbare typer kan ikke bruges i visse situationer, såsom i en instans af operator eller som elementer i et array.

Så hvis vi skriver:

List someList = new ArrayList<>();
boolean instanceTest = someList instanceof List<?>

Denne kode kompilerer og instanceTest er sandt .

Men hvis vi bruger instansen af operatør på Liste :

List anotherList = new ArrayList<>();
boolean instanceTest = anotherList instanceof List<? extends Object>;

så kompilerer linje 2 ikke.

På samme måde kompilerer linje 1 i nedenstående uddrag, men linje 2 gør det ikke:

List<?>[] arrayOfList = new List<?>[1];
List<? extends Object>[] arrayOfAnotherList = new List<? extends Object>[1]

6. Konklusion

I denne korte vejledning så vi lighederne og forskellene i og .

Selvom de for det meste ligner hinanden, er der subtile forskelle mellem de to med hensyn til, om de kan genskabes eller ej.


Java tag