Generics e wildcard Java

di il
8 risposte

Generics e wildcard Java

Avrei bisogno di un aiuto a capire bene i generics e le wildcard. Ho scritto precedentemente un testo lunghissimo, ma quando ho fatto invia mi ha cancellato tutto, è stato un pochino frustrante, quindi perdonatemi se ora sarò più conciso.
Ho recuperato alcuni esami passati e ci sono questi esercizi:

		List<Object> listOne;
		List<?> listTwo;
		List<? extends Number> listThree;
		List<? super Integer> listFour;
Quale tipologia di oggetto può essere recuperato/salvato nelle seguenti liste?
Le liste in java sono Invarianti quindi se ci aspettiamo un tipo T, dobbiamo avere per forza un tipo T e non un suo sottotipo come nel caso degli array che sono invarianti.
Quindi nella prima lista posso salvare per esempio ArrayList<Object>, mentre nel secondo posso salvare un ArrayList<T> ovvero un qualsiasi tipo di arraylist.
Nella terza lista ho covarianza quindi posso passare una qualsiasi lista che sia sottotipo di Number, mentre nella quarta lista ho controvarianza e posso passare qualsiasi lista sia supertipo di Integer.

Considerando le liste definite precedentemente, descrivere la correttezza dei seguenti comandi.
listOne.add(10);
		listTwo.add(10);
		Object o = listTwo.get(0);
		listThree.add(0);
		Number n = listThree.get(0);
		listFour.add(10);
		Number n1 = listFour.get(0);
Sui primi tre comandi ho alcuni dubbi. Il primo comando è valido perché un Integer è anche un object, il secondo e il terzo non so cosa rispondere.
Per quanto riguarda listThree siccome ho covarianza posso recuperare un valore perché sono sicuro di avere un Number, ma non aggiungere alcun valore perché non ne conosco effettivamente il tipo(per esempio ho una lista di Integer e sarebbe scorretto aggiungere un Number).
Per la listFour invece, avendo controvarianza, posso salvare un Integer perché è quello che mi aspetto, mentre non posso recuperare il valore perché non ho la certezza che sia effettivamente un Integer.

Inoltre che differenza c'è tra un parametro list<Object> e un parametro list<?> ?
Che garanzia ho su una variabile dichiata list<? extends ClassA>?
Potrebbe essere corretta come risposta ho la garanzia che ogni Oggetto della lista sia di tipo ClassA?
Come possiamo usare, invece, un oggetto del tipo Function<? super ClassA,Integer>?
Questa non so proprio risponderla scusate, non cerco il compitino risolto, ma ci ho perso una settimana dietro e ho l'esame a breve e ancora non capisco.
Grazie dell'aiuto

8 Risposte

  • Re: Generics e wildcard Java

    squizzi ha scritto:


    
    		List<Object> listOne;
    		List<?> listTwo;
    		List<? extends Number> listThree;
    		List<? super Integer> listFour;
    
    Quale tipologia di oggetto può essere recuperato/salvato nelle seguenti liste?
    1) List<Object> è sostanzialmente l'equivalente parametrizzato delle "vecchie" liste (pre Java 5). Prima di Java 5 in un List ci mettevi qualunque tipo di oggetto e lo estraevi "vedendolo" come Object. Con List<Object> è la stessa cosa: inserisci qualunque tipo di oggetto e lo estrai vedendolo come Object.


    2) List<?> NON è una lista che può contenere qualunque tipo di oggetto. Ma una lista di cui non si sa a priori la parametrizzazione concreta.

    List<?> listTwo;
    //...
    listTwo = new ArrayList<Number>();

    dal codice si capisce (lo vediamo noi) che viene assegnato un ArrayList<Number> a listTwo ma il compilatore, solo guardando alla variabile listTwo, NON lo può sapere. Se non lo si sa ..... c'è un limite: non puoi inserirci nulla nella lista! (con una sola eccezione: un null letterale) E puoi solo estrarre vedendo gli oggetti come Object in senso estremamente generalizzato.


    3) Per List<? extends Number> anche qui il "?" denota l'incertezza. Ma con una informazione in più: siamo certi che qualunque lista venga assegnata a listThree, dovrà essere di tipo <Number> o sottotipo.
    Anche qui vale la regola di prima: non sai cosa potrebbe essere assegnato e quindi non puoi inserire nulla. Non puoi inserire es. un Integer (perché se la lista assegnata fosse un ArrayList<Double> ??). Idem eccezione particolare: inserire un null letterale è possibile.
    Puoi solo estrarre vedendo gli oggetti come tipo del bound, ovvero Number (o ancora più sopra, es. assegnare ad una variabile Object).


    4) List<? super Integer> anche qui c'è l'incertezza ma è il contrario del precedente. Qualunque lista verrà assegnata a listFour dovrà per forza essere <Integer> o supertipo. Ma qualunque cosa sia sarà sicuramente in grado di contenere degli Integer. Quindi gli oggetti Integer si possono inserire!
    E puoi estrarre solo vedendo gli oggetti come Object in senso generalizzato.

    squizzi ha scritto:


    Le liste in java sono Invarianti quindi se ci aspettiamo un tipo T, dobbiamo avere per forza un tipo T e non un suo sottotipo come nel caso degli array che sono invarianti.
    Gli array sono "covarianti": un String[] È un sottotipo di Object[].
    I generics (non le liste!) sono invece di base "invarianti": un List<String> NON è un sottotipo di List<Object> .
    I bounded wildcard sono stati introdotti proprio per fornire una sorta di polimorfismo anche sui tipi parametrizzati.



    P.S. per chi non mi ha visto per un po': yes, I am back!
  • Re: Generics e wildcard Java

    Grazie mille della risposta, facevo confusione tra list e generics. Per gli Array ho sbagliato a scrivere.
    Per caso sai aiutarmi anche con le ultime mie domande?
    Nel caso della function avrò qualcosa del tipo
    (ClassA o superiore) =>{return 1;}
    L' unica risposta che mi viene in mente è che se all'Interno delle parentesi ho chiamate ai metodi toString o equals, il tipo di quella funzione diventerebbe function<Object, Integer> quindi se avessi avuto una function<? extends ClassA> ci sarebbe stato un errore
  • Re: Generics e wildcard Java

    squizzi ha scritto:


    Inoltre che differenza c'è tra un parametro list<Object> e un parametro list<?> ?
    Se un metodo ha come parametro un List<Object>, gli puoi passare un ArrayList<Object>, LinkedList<Object> ecc.. ma appunto SOLO <Object> (causa l'"invarianza" dei generics). E il metodo potrà estrarre gli oggetti come Object.

    Se un metodo ha come parametro un List<?>, gli puoi passare qualunque lista con qualunque parametrizzazione es. ArrayList<String>, LinkedList<Date>, Vector<Locale> (sì, Vector è un List) ecc.. E il metodo potrà estrarre gli oggetti come Object.

    squizzi ha scritto:


    Che garanzia ho su una variabile dichiata list<? extends ClassA>?
    Che sicuramente puoi estrarre e "vedere" gli oggetti come tipo del bound, ovvero ClassA. Ma non puoi inserire nulla (eccetto null letterale).

    squizzi ha scritto:


    Potrebbe essere corretta come risposta ho la garanzia che ogni Oggetto della lista sia di tipo ClassA?
    Gli oggetti contenuti POSSONO essere sottotipi di ClassA, non è quello il punto. Tu puoi estrarli vedendoli solo come del tipo ClassA.

    squizzi ha scritto:


    Come possiamo usare, invece, un oggetto del tipo Function<? super ClassA,Integer>?
    Function<? super ClassA,Integer> descrive solo un tipo a cui è possibile assegnare una "funzione" in cui il parametro di ingresso è un ClassA (o supertipo) e l'uscita è un valore di tipo Integer.
    Ma dove possa essere usato ... dipende ... (anche da cosa è ClassA).

    Mi viene in mente ora solo un esempio con una type variable T e un metodo "generico":
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Function;
    
    public class Prova {
        public static void main(String[] args) {
            List<String> stringhe = Arrays.asList("000123", "000789");
    
            Function<CharSequence,Integer> cstoi = seq -> Integer.valueOf(seq.toString());
    
            List<Integer> interi = convertToIntegers(stringhe, cstoi);
            System.out.println(stringhe);
            System.out.println(interi);
        }
    
        public static <T> List<Integer> convertToIntegers(List<T> input, Function<? super T,Integer> func) {
            List<Integer> output = new ArrayList<Integer>();
    
            for (T v : input) {
                Integer r = func.apply(v);
                output.add(r);
            }
    
            return output;
        }
    }
    Se il parametro func fosse Function<T,Integer> (quindi senza "? super"), sarebbe lecito ma se l'input è un List<String>, allora DEVO passare una funzione Function<String,Integer>.

    Usando ? super T la forma è più ampia. Se passo un List<String> mi è possibile passare un Function<CharSequence,Integer> perché il apply di Function diventa sostanzialmente Integer apply(CharSequence) per cui appunto un String, essendo un CharSequence, sarebbe lecito come argomento.
  • Re: Generics e wildcard Java

    Grazie mille, gentilissimo. L'unica cosa è per la Function, non mi è ancora chiarissimo, il professore faceva l'esempio di utilizzo di un Predicate<Employee>
    Posso utilizzare (e)->{return e.getSalary()>1000;}
    ma non potevo fare per esempio (e)->{return e.toString()%2==0;}
    Perché è un Predicate<Object> e quindi era necessario dichiarare Predicate<? super Employee>
    Non ho capito perché mi dovrebbe dare problemi visto che Employee comunque eredita quei metodi.
    Penso che finché non sbatto la testa a lavorare su queste cose e mi imbatto ne problemi che ci sono non riesco a capire. Rimane solo una cosa teorica purtroppo. Ho realizzato anche il progetto per questo esame, ma non ho utilizzato niente di generics, forse solo per implementare l'algoritmo Minimax, ma non era necessario.
  • Re: Generics e wildcard Java

    squizzi ha scritto:


    ma non potevo fare per esempio (e)->{return e.toString()%2==0;}

    visto che Employee comunque eredita quei metodi.
    Certo, Employee ha sicuramente un toString() (ridefinito o ereditato che sia).
    Ma non è quello il punto. Non puoi usare una stringa con %2 !
    Ovvero xyz.toString()%2 non ha senso già di per sé.
  • Re: Generics e wildcard Java

    Si scusa ho dimenticato .length() alla fine..scrivo dal cellulare e non è il massimo.
    Però che senso ha definire un Predicate<Employee> e Predicate<? super Employee>?
    Forse se definisco un metodo
    public void f(Predicate<Employee> p, Employee e){
    If(p.test(e)) return System.out.println("");
    }

    E nel main faccio una cosa del genere

    f((e)-> {return e.toString(). length ()==2 ;}, employee)

    Questo potrebbe darmi errori? Scusa se ho scritto male qualcosa, ma sono da telefono.
  • Re: Generics e wildcard Java

    squizzi ha scritto:


    Però che senso ha definire un Predicate<Employee> e Predicate<? super Employee>?
    Se un metodo ha come parametro un Predicate<? super Employee> vuol dire che tu gli puoi passare un Predicate<Employee> oppure un altro predicate la cui parametrizzazione concreta è "sopra" Employee (es. Object).
    Se Employee deriva direttamente da Object, a parte un Predicate<Employee> (che ha molto senso), potresti solo passare un Predicate<Object> che invece ha relativamente poco senso perché la logica del predicato la puoi fare solo con i metodi di Object.

    Sarebbe più utile in uno scenario del genere:
    public class Person {
        private String firstName;
        private String lastName;
        private int birthYear;
        // ........
    }
    
    public class Employee extends Person {
        // ... altro specifico di un employee
    }
    Se tu poi facessi un metodo es.:

    public static int countEmployeesBy(List<Employee> employees, Predicate<? super Employee> predicate)

    A questo countEmployeesBy puoi passare un Predicate<Person> che "accetta" la persona ad esempio se il birthYear è > 1970. In pratica la forma ? super Employee ti permette di usare un Predicate<Person> ANCHE per trattare oggetti Employee.

    Se il metodo fosse stato invece:

    public static int countEmployeesBy(List<Employee> employees, Predicate<Employee> predicate)

    Gli puoi passare solo un Predicate<Employee> .

    Prova un "esercizio" del genere.

    squizzi ha scritto:


    f((e)-> {return e.toString(). length ()==2 ;}, employee)
    Scusa ma che senso avrebbe usare il length sulla stringa del toString()? Che cosa dà toString()?
  • Re: Generics e wildcard Java

    Il professore ha presentato il codice così, non ha per nulla senso quel metodo.
    Da come l'hai spiegato tu ora ho capito.
    Grazie mille della pazienza. Hai risolto tutti i miei dubbi.
Devi accedere o registrarti per scrivere nel forum
8 risposte