Dubbio classe generica

di il
19 risposte

Dubbio classe generica

Salve

ho il seguente codice
public class NomeClasse<T> {

}
e nel main

NomeClasse oggetto = new NomeClasse();
ho istanziato la classe senza specificare il tipo generico, mi sarei aspettato un errore ma funziona a meravigla, in questo caso il tipo passato è Object?


inoltre vorrei capire la differenza tra queste deu righe
NomeClasse oggetto = new NomeClasse<Integer>();
NomeClasse<Object> oggetto = new NomeClasse<Integer>();
la seconda da errore, e ne capisco il motivo, ciò che non capisco è perchè la prima non da errore

grazie

19 Risposte

  • Re: Dubbio classe generica

    Giovx24 ha scritto:


    
    NomeClasse oggetto = new NomeClasse();
    
    ho istanziato la classe senza specificare il tipo generico, mi sarei aspettato un errore ma funziona a meravigla, in questo caso il tipo passato è Object?
    No non c'è un "errore". Come l'hai usato tu si chiama il raw type (tipo "crudo" se vogliamo tradurlo). Ovvero utilizzare senza parametrizzazione concreta un tipo che invece è "generico". Questo è stato reso lecito per questioni di compatibilità all'indietro.
    Ma sarebbe da evitare, perché causa warning più avanti, quando poi usi quella variabile.

    Giovx24 ha scritto:


    inoltre vorrei capire la differenza tra queste deu righe
    NomeClasse oggetto = new NomeClasse<Integer>();
    NomeClasse<Object> oggetto = new NomeClasse<Integer>();
    La prima è tecnicamente corretta e lecita, il raw type (solo NomeClasse) è il super-tipo di tutte le parametrizzazioni possibili di quel tipo. NomeClasse<Integer>, NomeClasse<String>, NomeClasse<Date> ecc... sono sottotipi di NomeClasse (raw type).

    Ma hai comunque un raw type e come ho già detto prima, sarebbe da evitare.

    La seconda è SBAGLIATA. Per come sono stati architettati i generics (per via della "erasure"), essi non sono "covarianti". Ovvero ad esempio un List<String> NON è un sottotipo di List<Object>.
    Questo è ben diverso da quello che avviene per gli array, che invece sono covarianti. Un array String[] È un sottotipo di Object[] .

    Per farti capire il perché ci vorrebbe una spiegazione un pochino più lunga, mettendo a confronto cosa succede con gli array e con i tipi generici.
  • Re: Dubbio classe generica

    Grazie

    credo di aver intuito

    volevo anche chiedere la differenza tra queste due implementazioni
    public interface InterfaceName<T> {
    
    }
    public class ClassName implements InterfaceName<Object> {
    
    }
    public class ClassName implements InterfaceName{
    
    }
  • Re: Dubbio classe generica

    Giovx24 ha scritto:


    credo di aver intuito
    Se sai e hai intuito giusto, dovresti sapere cosa succede alla 3° riga di questo codice:
    String[] strArr = new String[10];
    Object[] objArr = strArr;
    objArr[0] = new Integer(123);
    Che invece per i generics NON potrebbe esserci un equivalente.

    Giovx24 ha scritto:


    volevo anche chiedere la differenza tra queste due implementazioni
    public interface InterfaceName<T> {}
    public class ClassName implements InterfaceName<Object> {}
    public class ClassName implements InterfaceName{}
    In questo caso specifico, cioè raw-type vs <Object>, ai fini della implementazione di un eventuale metodo, non cambierebbe nulla.

    Ovvero, se InterfaceName fosse:
    public interface InterfaceName<T> {
        void prova(T t);
    }
    Sia con implements InterfaceName<Object> che con implements InterfaceName il metodo dovresti implementarlo così:

    @Override
    public void prova(Object t) { ..... }
  • Re: Dubbio classe generica

    andbin ha scritto:


    Giovx24 ha scritto:


    credo di aver intuito
    Se sai e hai intuito giusto, dovresti sapere cosa succede alla 3° riga di questo codice:
    String[] strArr = new String[10];
    Object[] objArr = strArr;
    objArr[0] = new Integer(123);
    Che invece per i generics NON potrebbe esserci un equivalente.

    Giovx24 ha scritto:


    volevo anche chiedere la differenza tra queste due implementazioni
    public interface InterfaceName<T> {}
    public class ClassName implements InterfaceName<Object> {}
    public class ClassName implements InterfaceName{}
    In questo caso specifico, cioè raw-type vs <Object>, ai fini della implementazione di un eventuale metodo, non cambierebbe nulla.

    Ovvero, se InterfaceName fosse:
    public interface InterfaceName<T> {
        void prova(T t);
    }
    Sia con implements InterfaceName<Object> che con implements InterfaceName il metodo dovresti implementarlo così:

    @Override
    public void prova(Object t) { ..... }
    no non saprei, mi aspetterei un errore alla terza riga
    objArr[0] = new Integer(123);
    perchè stiamo cercando si assegnare un intero ad una stringa.

    grazie
  • Re: Dubbio classe generica

    Beh ho appena visto che funziona, nel dare la risposta ho pensato troppo a basso livello

    pensavo che facendo l'assegnamento tra i due array, objArr diventasse un riferimento ad un array di 10 riferimenti a stringhe.
    ma a quanto pare objArr diventa un riferimento ad un array di 10 riferimenti ad Object che puntano a delle stringhe.

    mi verrebbe da pensare che nel fare l'assegnamento tra array non cambia solo il valore di objArr ma l'array dei 10 riferimenti viene copiato e ce nè siano due nell'heap.
    ma forse sto delirando
  • Re: Dubbio classe generica

    Giovx24 ha scritto:


    no non saprei, mi aspetterei un errore alla terza riga
    objArr[0] = new Integer(123);
    perchè stiamo cercando si assegnare un intero ad una stringa.
    Sì, più o meno ci sei. Ma la vera spiegazione è la seguente.

    Quando crei un array di String con es. new String[10] sul heap-space, in memoria, oltre allo spazio per i 10 elementi, la JVM mantiene all'interno dell'oggetto array anche le informazioni sul tipo. Ovvero c'è scritto in un qualche modo "i miei elementi sono di tipo String". E questa informazione viene USATA dalla JVM per controllare ad ogni assegnamento degli elementi se il tipo è corretto.

    Quando fai:
    Object[] objArr = strArr;

    questo è lecito per le regole di Java. Avendo un Object[], teoricamente potrebbe contenere qualunque cosa. Ma l'array realmente istanziato è un String[] e le informazioni su questo tipo ci sono tutte. Ovvero gli array sono "completamente rappresentati" a runtime.

    Quindi quando tenti di fare:
    objArr[0] = new Integer(123);

    ti becchi un ArrayStoreException, la JVM grazie a quelle informazioni mantenute nell'array, verifica che Integer non è compatibile con String e lancia questa eccezione specifica per questo.


    Ora, vediamo i generics, con un List (ma sarebbe similare per qualunque altro tipo):

    List<String> strList = new ArrayList<String>(); // OK

    Se (e ripeto SE) la seguente riga fosse lecita (NON lo è in realtà):

    List<Object> objList = strList;

    tu poi potresti "tentare" di mettere nel List<Object> dei Integer. E .... ci riusciresti. Perché quando fai new ArrayList<String>(), all'interno dell'oggetto ArrayList NON c'è alcuna informazione che dica "i miei elementi sono di tipo String". Questo è dovuto alla "erasure", a come sono implementati i generics.

    Se fosse possibile, si potrebbe combinare casini nella lista. E tutta la teoria dei generics andrebbe a farsi friggere in padella ....

    Questo in sostanza è il motivo per cui è stato PROIBITO che List<String> sia un sottotipo di List<Object>.
  • Re: Dubbio classe generica

    Giovx24 ha scritto:


    Beh ho appena visto che funziona, nel dare la risposta ho pensato troppo a basso livello

    pensavo che facendo l'assegnamento tra i due array, objArr diventasse un riferimento ad un array di 10 riferimenti a stringhe.
    ma a quanto pare objArr diventa un riferimento ad un array di 10 riferimenti ad Object che puntano a delle stringhe.

    mi verrebbe da pensare che nel fare l'assegnamento tra array non cambia solo il valore di objArr ma l'array dei 10 riferimenti viene copiato e ce nè siano due nell'heap.
    ma forse sto delirando
    ah no l'errore è a run-time
  • Re: Dubbio classe generica

    andbin ha scritto:


    Giovx24 ha scritto:


    no non saprei, mi aspetterei un errore alla terza riga
    objArr[0] = new Integer(123);
    perchè stiamo cercando si assegnare un intero ad una stringa.
    Sì, più o meno ci sei. Ma la vera spiegazione è la seguente.

    Quando crei un array di String con es. new String[10] sul heap-space, in memoria, oltre allo spazio per i 10 elementi, la JVM mantiene all'interno dell'oggetto array anche le informazioni sul tipo. Ovvero c'è scritto in un qualche modo "i miei elementi sono di tipo String". E questa informazione viene USATA dalla JVM per controllare ad ogni assegnamento degli elementi se il tipo è corretto.

    Quando fai:
    Object[] objArr = strArr;

    questo è lecito per le regole di Java. Avendo un Object[], teoricamente potrebbe contenere qualunque cosa. Ma l'array realmente istanziato è un String[] e le informazioni su questo tipo ci sono tutte. Ovvero gli array sono "completamente rappresentati" a runtime.

    Quindi quando tenti di fare:
    objArr[0] = new Integer(123);

    ti becchi un ArrayStoreException, la JVM grazie a quelle informazioni mantenute nell'array, verifica che Integer non è compatibile con String e lancia questa eccezione specifica per questo.


    Ora, vediamo i generics, con un List (ma sarebbe similare per qualunque altro tipo):

    List<String> strList = new ArrayList<String>(); // OK

    Se (e ripeto SE) la seguente riga fosse lecita (NON lo è in realtà):

    List<Object> objList = strList;

    tu poi potresti "tentare" di mettere nel List<Object> dei Integer. E .... ci riusciresti. Perché quando fai new ArrayList<String>(), all'interno dell'oggetto ArrayList NON c'è alcuna informazione che dica "i miei elementi sono di tipo String". Questo è dovuto alla "erasure", a come sono implementati i generics.

    Se fosse possibile, si potrebbe combinare casini nella lista. E tutta la teoria dei generics andrebbe a farsi friggere in padella ....

    Questo in sostanza è il motivo per cui è stato PROIBITO che List<String> sia un sottotipo di List<Object>.

    okok ho capito tutto, trlascia ciò che ho detto prima

    grazie
  • Re: Dubbio classe generica

    Giovx24 ha scritto:


    okok ho capito tutto, trlascia ciò che ho detto prima
    Benissimo. Se hai compreso questo aspetto, hai già una buona base sui generics che ti permetterà di capire meglio tutto il resto. Ad esempio i "bounds" e i "wildcard" che sono stati introdotti per fornire una sorta di polimorfismo (limitato) anche con i generics, per "girare" attorno al fatto che non sono covarianti.
  • Re: Dubbio classe generica

    andbin ha scritto:


    Giovx24 ha scritto:


    okok ho capito tutto, trlascia ciò che ho detto prima
    Benissimo. Se hai compreso questo aspetto, hai già una buona base sui generics che ti permetterà di capire meglio tutto il resto. Ad esempio i "bounds" e i "wildcard" che sono stati introdotti per fornire una sorta di polimorfismo (limitato) anche con i generics, per "girare" attorno al fatto che non sono covarianti.
    si a quanto ho capito posso in qualche modo bypassare così
    List<? extends Object> l = new ArrayList<String>();
    
    grazie
  • Re: Dubbio classe generica

    Non posso dire di aver capito cos'è il raw type, ma credo non sia importante per me
  • Re: Dubbio classe generica

    Giovx24 ha scritto:


    si a quanto ho capito posso in qualche modo bypassare in questo modo
    List<? extends Object> l = new ArrayList<String>();
    
    Sì, questo è lecito, corretto. MA ha le sue limitazioni.
    Prova a dirmi, puoi fare:

    l.add("Ciao");

    ??
  • Re: Dubbio classe generica

    andbin ha scritto:


    Giovx24 ha scritto:


    si a quanto ho capito posso in qualche modo bypassare in questo modo
    List<? extends Object> l = new ArrayList<String>();
    
    Sì, questo è lecito, corretto. MA ha le sue limitazioni.
    Prova a dirmi, puoi fare:

    l.add("Ciao");

    ??
    a quanto pare no

    allora a che serve?

    grazie
  • Re: Dubbio classe generica

    Giovx24 ha scritto:


    a quanto pare no

    allora a che serve?
    Usato come l'hai usato tu (cioè un nuovo ArrayList<String> assegnato subito ad un List<? extends Object>) davvero molto poco.
    E' più facile/tipico usare i bound ad esempio nei parametri a metodi/costruttori.

    Innanzitutto sappi che <? extends Object> è semplicemente la forma "estesa" di <?> ma sono la stessa cosa e con gli stessi limiti.

    Ecco uno scenario più sensato. Voglio fare un metodo che riceve una lista di oggetti Number ed ottenere la somma come double fisso.

    Se facessi:

    public static double somma(List<Number> numeri) { ... }

    potrei passare SOLO qualcosa che è <Number>, non potrei passare es. un ArrayList<Float> . E sarebbe molto MOLTO limitante ....

    Allora si fa così:

    public static double somma(List<? extends Number> numeri) { ... }

    Allora posso passare un ArrayList<Integer>, ArrayList<Float>, LinkedList<Double>, ecc... insomma qualunque parametrizzazione il cui tipo è "sotto" (o uguale) Number.
    Nota che all'interno del metodo gli oggetti li posso "vedere" solo come Number. Ma Number ha doubleValue() e potrei fare benissimo la somma in double.
Devi accedere o registrarti per scrivere nel forum
19 risposte