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

Brug af T forlænger U?

Jeg tror faktisk, at dette kun giver mening, når metodens typeparameter vises som typeparameteren for en parametreret type, der er en del af metodesignaturen.

(Jeg kunne i hvert fald ikke hurtigt komme med et eksempel, hvor det virkelig ville giver mening ellers)

Dette er også tilfældet i det spørgsmål, du linkede til, hvor metodetypeparametrene bruges som typeparametre i AutoBean klasse.

En lille opdatering:

Baseret på diskussionen i spørgsmålet og andre svar, var kernen i dette spørgsmål sandsynligvis en fejlfortolkning af måden, hvordan typeparametrene bruges på. Som sådan kunne dette spørgsmål betragtes som et duplikat af betydningen af ​​ i java-funktionserklæringen, men forhåbentlig vil nogen betragte dette svar som nyttigt alligevel.

I sidste ende grunden til at bruge mønsteret <T, U extends T> kan ses i arveforhold af parametriserede typer, som i detaljer kan være ret komplicerede. Som et eksempel, for at illustrere det mest relevante punkt:A List<Integer> er ikke en undertype af en List<Number> .

Et eksempel, der viser, hvor det kan gøre en forskel, er nedenfor. Den indeholder en "triviel" implementering, der altid virker (og ikke giver mening, så vidt jeg kan se). Men typebindingen bliver relevant, når typeparametrene T og U er også typeparametrene for metodeparametrene og returtype. Med T extends U , kan du returnere en type, der har en supertype som typeparameter. Ellers kunne du ikke, som vist med eksemplet // Does not work :

import java.util.ArrayList;
import java.util.List;

public class SupertypeMethod {
    public static void main(String[] args) {

        Integer integer = null;
        Number number = null;

        List<Number> numberList = null;
        List<Integer> integerList = null;

        // Always works:
        integer = fooTrivial(integer);
        number = fooTrivial(number);
        number = fooTrivial(integer);

        numberList = withList(numberList);
        //numberList = withList(integerList); // Does not work

        // Both work:
        numberList = withListAndBound(numberList);
        numberList = withListAndBound(integerList);
    }

    public static <T, U extends T> T fooTrivial(U u) {
        return u;
    }

    public static <T, U extends T> List<T> withListAndBound(List<U> u) {
        List<T> result = new ArrayList<T>();
        result.add(u.get(0));
        return result;
    }

    public static <T> List<T> withList(List<T> u) {
        List<T> result = new ArrayList<T>();
        result.add(u.get(0));
        return result;
    }

}

(Dette ser selvfølgelig en smule konstrueret ud, men jeg tror, ​​at man kunne forestille sig scenarier, hvor det rent faktisk giver mening)


Dette er praktisk, når du vil returnere en supertype; præcis som du viste i dit eksempel.

Du tager en U som input og returner en T - som er en supertype af U; den anden vej rundt for at erklære dette ville være T super U - men dette er ikke lovligt i java.

Dette burde være et eksempel på, hvad jeg faktisk mener. Antag en meget simpel klasse som:

static class Holder<T> {

    private final T t;

    public Holder(T t) {
        this.t = t;
    }

    public <U super T> U whenNull(U whenNull){
        return t == null ? whenNull : t;
    }
}

Metode whenNull som det er defineret ville ikke kompilere, da U super T er ikke tilladt i java.

I stedet kan du tilføje en anden typeparameter og invertere typerne:

static class Holder<U, T extends U> {

    private final T t;

    public Holder(T t) {
        this.t = t;
    }

    public U whenNull(U whenNull) {
        return t == null ? whenNull : t;
    }
}

Og brugen ville være:

Holder<Number, Integer> n = new Holder<>(null);
Number num = n.whenNull(22D);

dette giver mulighed for at returnere en super type; men det ser meget mærkeligt ud. Vi har tilføjet en anden type i klasseerklæringen.

Vi kunne ty til:

static class Holder<T> {

    private final T t;

    public Holder(T t) {
        this.t = t;
    }

    public static <U, T extends U> U whenNull(U whenNull, Holder<T> holder) {
        return holder.t == null ? whenNull : holder.t;
    }
}

eller endda gøre denne metode statisk.

For en eksisterende begrænsning kan du prøve at gøre:

Optional.ofNullable(<SomeSubTypeThatIsNull>)
        .orElse(<SomeSuperType>)

Min første tanke var:for pokker,

Number n = Baz.bar(2);

ville "altid" virke, da Integer udvider Number. Så det er der ingen fordel ved at gøre. Men hvad nu hvis du havde en super klasse, der ikke var abstrakt?!

Derefter U extends T giver dig mulighed for at returnere et objekt, der kun er af supertypeklassen, men ikke af børneklassen!

Noget som

class B { } 
class C extends B { }

nu kan den generiske metode også returnere en forekomst af B. Hvis der kun er et T ... så kan metoden kun returnere forekomster af C.

Med andre ord:U extends T giver dig mulighed for at returnere forekomster af B og C. T alene:kun C!

Men selvfølgelig giver ovenstående mening, når man ser på nogle specifikke B og C. Men når en metode (i virkeligheden) blot returnerer en forekomst af B, hvorfor skulle man så have brug for generika her i første omgang?!

Så jeg er enig i spørgsmålet:Jeg kan ikke se det praktiske værdien af ​​denne konstruktion enten. Medmindre man kommer i refleksion, men selv da ser jeg ikke et lyddesign, der kun kunne arbejde på grund af U extends T .


Java tag