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

Hvad er forskellen mellem <? forlænger Base> og <T forlænger Base>?

Ved at definere metoden med følgende signatur:

static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

og påberåber det som:

compiles(new HashMap<Integer, List<Integer>>());

du matcher T mod den type, du angiver.

I jls §8.1.2 finder vi, at (interessant del med fed skrift):

En generisk klasseerklæring definerer et sæt parameteriserede typer (§4.5), en for hver mulig påkaldelse af typeparameterafsnittet efter typeargumenter . Alle disse parametriserede typer deler den samme klasse ved kørsel.

Med andre ord, typen T er matchet med inputtypen og tildelt Integer . Signaturen bliver i praksis static void compiles(Map<Integer, List<Integer>> map) .

Når det kommer til doesntCompile metode, jls definerer regler for underskrivning (§4.5.1, fed af mig):

Et typeargument T1 siges at indeholde et andet typeargument T2, skrevet T2 <=T1, hvis mængden af ​​typer angivet med T2 beviseligt er en delmængde af mængden af ​​typer angivet med T1 under den refleksive og transitive lukning af følgende regler ( hvor <:angiver undertypebestemmelse (§4.10)):

  • ? forlænger T <=? forlænger S hvis T <:S

  • ? forlænger T <=?

  • ? super T <=? super S hvis S <:T

  • ? super T <=?

  • ? super T <=? udvider Objekt

  • T <=T

  • T <=? forlænger T

  • T <=? super T

Det betyder, at ? extends Number indeholder faktisk Integer eller endda List<? extends Number> indeholder List<Integer> , men det er ikke tilfældet for Map<Integer, List<? extends Number>> og Map<Integer, List<Integer>> . Mere om det emne kan findes i denne SO-tråd. Du kan stadig lave versionen med ? jokertegn arbejde ved at erklære, at du forventer en undertype af List<? extends Number> :

public class Example {
    // now it compiles
    static void doesntCompile(Map<Integer, ? extends List<? extends Number>> map) {}
    static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

    public static void main(String[] args) {
        doesntCompile(new HashMap<Integer, List<Integer>>());
        compiles(new HashMap<Integer, List<Integer>>());
    }
}

I opkaldet:

compiles(new HashMap<Integer, List<Integer>>());

T matches med heltal, så argumentets type er en Map<Integer,List<Integer>> . Det er ikke tilfældet for metoden doesntCompile :Argumentets type forbliver Map<Integer, List<? extends Number>> uanset det faktiske argument i opkaldet; og det kan ikke tildeles fra HashMap<Integer, List<Integer>> .

OPDATERING

I doesntCompile metode, intet forhindrer dig i at gøre noget som dette:

static void doesntCompile(Map<Integer, List<? extends Number>> map) {
    map.put(1, new ArrayList<Double>());
}

Så åbenbart kan den ikke acceptere en HashMap<Integer, List<Integer>> som argumentet.


Simpelt eksempel på demonstration. Samme eksempel kan visualiseres som nedenfor.

static void demo(List<Pair<? extends Number>> lst) {} // doesn't work
static void demo(List<? extends Pair<? extends Number>> lst) {} // works
demo(new ArrayList<Pair<Integer>()); // works
demo(new ArrayList<SubPair<Integer>()); // works for subtype too

public static class Pair<T> {}
public static class SubPair<T> extends Pair<T> {}

List<Pair<? extends Number>> er en jokertegn på flere niveauer, hvorimod List<? extends Number> er en standard jokertegn type .

Gyldige konkrete instanser af wild card-typen List<? extends Number> inkludere Number og eventuelle undertyper af Number hvorimod i tilfælde af List<Pair<? extends Number>> som er et typeargument af typeargument og i sig selv har en konkret instansiering af den generiske type.

Generiske er invariante, så Pair<? extends Number> wild card type kan kun acceptere Pair<? extends Number>> . Indvendig type ? extends Number er allerede samvariant. Du skal lave den omsluttende type som kovarian for at tillade kovarians.


Java tag