Come usare la classe <T> in Java?


247

C'è una buona discussione su Generics e su cosa fanno realmente dietro le quinte di questa domanda , quindi sappiamo tutti che Vector<int[]>è un vettore di array interi, ed HashTable<String, Person>è una tabella le cui chiavi sono stringhe e valori Persons. Tuttavia, ciò che mi sorprende è l'uso di Class<>.

La classe java Classdovrebbe anche prendere un nome modello (o così mi viene detto dalla sottolineatura gialla in eclissi). Non capisco cosa dovrei mettere lì. L'intero punto Classdell'oggetto è quando non si dispone completamente delle informazioni su un oggetto, per la riflessione e simili. Perché mi fa specificare quale classe Classconterrà l' oggetto? Chiaramente non lo so, o non userei l' Classoggetto, userei quello specifico.

Risposte:


136

L'uso della versione generata della classe Class consente, tra le altre cose, di scrivere cose del genere

Class<? extends Collection> someCollectionClass = someMethod();

e quindi puoi essere sicuro che l'oggetto Class che ricevi si estende Collectione un'istanza di questa classe sarà (almeno) una Collection.


183

Tutto ciò che sappiamo è " Tutte le istanze di una qualsiasi classe condividono lo stesso oggetto java.lang.Class di quel tipo di classe "

per esempio)

Student a = new Student();
Student b = new Student();

Quindi a.getClass() == b.getClass()è vero.

Ora supponiamo

Teacher t = new Teacher();

senza generici è possibile il seguito.

Class studentClassRef = t.getClass();

Ma adesso è sbagliato ..?

es.) public void printStudentClassInfo(Class studentClassRef) {}può essere chiamato conTeacher.class

Questo può essere evitato usando i generici.

Class<Student> studentClassRef = t.getClass(); //Compilation error.

Ora cos'è T ?? T è parametri di tipo (chiamati anche variabili di tipo); delimitato da parentesi angolari (<>), segue il nome della classe.
T è solo un simbolo, come un nome di variabile (può essere qualsiasi nome) dichiarato durante la scrittura del file di classe. Successivamente, T verrà sostituito con un
nome di classe valido durante l'inizializzazione ( HashMap<String> map = new HashMap<String>();)

per esempio) class name<T1, T2, ..., Tn>

Quindi Class<T>rappresenta un oggetto di classe di tipo di classe specifico ' T'.

Supponi che i tuoi metodi di classe debbano funzionare con parametri di tipo sconosciuti come di seguito

/**
 * Generic version of the Car class.
 * @param <T> the type of the value
 */
public class Car<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Qui T può essere usato come Stringtipo come CarName

O T può essere usato come Integertipo come modelNumber ,

O T può essere usato come Objecttipo come istanza valida dell'auto .

Ora qui sopra è il semplice POJO che può essere utilizzato in modo diverso in fase di esecuzione.
Collezioni ad es.) Elenco, Set, Hashmap sono i migliori esempi che funzioneranno con oggetti diversi secondo la dichiarazione di T, ma una volta che abbiamo dichiarato T come String
ad es.) HashMap<String> map = new HashMap<String>();Quindi accetterà solo oggetti di istanza di String Class.

Metodi generici

I metodi generici sono metodi che introducono i propri parametri di tipo. Questo è simile alla dichiarazione di un tipo generico, ma l'ambito del parametro type è limitato al metodo in cui è dichiarato. Sono consentiti metodi generici statici e non statici, nonché costruttori di classi generiche.

La sintassi per un metodo generico include un parametro di tipo, parentesi angolari interne e appare prima del tipo restituito dal metodo. Per i metodi generici, la sezione dei parametri di tipo deve essere visualizzata prima del tipo restituito dal metodo.

 class Util {
    // Generic static method
    public static <K, V, Z, Y> boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

 class Pair<K, V> {

    private K key;
    private V value;
}

Ecco <K, V, Z, Y>la dichiarazione dei tipi usati negli argomenti del metodo che dovrebbe essere prima del tipo restituito che è booleanqui.

Nel seguito; la dichiarazione di tipo <T>non è richiesta a livello di metodo, poiché è già dichiarata a livello di classe.

class MyClass<T> {
   private  T myMethod(T a){
       return  a;
   }
}

Ma di seguito è sbagliato poiché i parametri di tipo a livello di classe K, V, Z e Y non possono essere utilizzati in un contesto statico (metodo statico qui).

class Util <K, V, Z, Y>{
    // Generic static method
    public static  boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

ALTRI SCENARI VALIDI SONO

class MyClass<T> {

        //Type declaration <T> already done at class level
        private  T myMethod(T a){
            return  a;
        }

        //<T> is overriding the T declared at Class level;
        //So There is no ClassCastException though a is not the type of T declared at MyClass<T>. 
        private <T> T myMethod1(Object a){
                return (T) a;
        }

        //Runtime ClassCastException will be thrown if a is not the type T (MyClass<T>).  
        private T myMethod1(Object a){
                return (T) a;
        }       

        // No ClassCastException        
        // MyClass<String> obj= new MyClass<String>();
        // obj.myMethod2(Integer.valueOf("1"));
        // Since type T is redefined at this method level.
        private <T> T myMethod2(T a){
            return  a;
        }

        // No ClassCastException for the below
        // MyClass<String> o= new MyClass<String>();
        // o.myMethod3(Integer.valueOf("1").getClass())
        // Since <T> is undefined within this method; 
        // And MyClass<T> don't have impact here
        private <T> T myMethod3(Class a){
            return (T) a;
        }

        // ClassCastException for o.myMethod3(Integer.valueOf("1").getClass())
        // Should be o.myMethod3(String.valueOf("1").getClass())
    private  T myMethod3(Class a){
        return (T) a;
    }


        // Class<T> a :: a is Class object of type T
        //<T> is overriding of class level type declaration; 
        private <T> Class<T> myMethod4(Class<T> a){
            return  a;
        }
    }

E infine il metodo statico necessita sempre di una <T>dichiarazione esplicita ; Non deriverà dal livello di classe Class<T>. Ciò è dovuto al livello di classe T associato all'istanza.

Leggi anche Restrizioni su Generics

Caratteri jolly e sottotitoli

argomento tipo per un metodo generico



"Classe <T> rappresenta un oggetto classe di tipo di classe specifico 'T'." Ha senso. Grazie ..
entro

Questa risposta è terribilmente confusa nel modo in cui usa le Classi (da scuola) in una domanda sulle Classi (in Java). È difficile sapere di cosa parla l'autore da una frase all'altra.
Echox,

@Echox Mi dispiace, posso migliorarlo, se hai domande specifiche.
Kanagavelu Sugumar,


32

Dalla documentazione Java:

[...] Più sorprendentemente, la classe Class è stata generata. I letterali di classe ora funzionano come token di tipo, fornendo informazioni sul tipo di runtime e di compilazione. Ciò consente uno stile di fabbriche statiche esemplificato dal metodo getAnnotation nella nuova interfaccia AnnotatedElement:

<T extends Annotation> T getAnnotation(Class<T> annotationType); 

Questo è un metodo generico. Deriva il valore del suo parametro di tipo T dal suo argomento e restituisce un'istanza appropriata di T, come illustrato dal frammento seguente:

Author a = Othello.class.getAnnotation(Author.class);

Prima dei generici, avresti dovuto trasmettere il risultato all'autore. Inoltre non avresti avuto modo di far verificare al compilatore che il parametro effettivo rappresentasse una sottoclasse di Annotation. [...]

Bene, non ho mai dovuto usare questo tipo di cose. Chiunque?


2
Io (pensavo di averlo fatto). Un framework (di sorta) con cui ho lavorato ti ha richiesto di passare il nome di classe dei servizi da cui dipendeva il tuo modulo. Ho creato un livello sopra quello che ha preso gli oggetti Class, in modo da limitare la quantità di scelte. Usando la Class<? extends X>notazione ho pensato di poterlo limitare solo ai tipi di "servizio". Tranne il fatto che non esisteva un tipo di "servizio" comune, quindi ho potuto farlo solo con Class<?>. Ahimè.
fwielstra,

9

Ho trovato class<T>utile quando creo ricerche nel registro dei servizi. Per esempio

<T> T getService(Class<T> serviceClass)
{
    ...
}

5

Come sottolineato da altre risposte, ci sono molte e buone ragioni per cui questo è classstato reso generico. Tuttavia ci sono molte volte in cui non hai modo di conoscere il tipo generico da usare Class<T>. In questi casi, puoi semplicemente ignorare gli avvisi di eclissi gialli o puoi usare Class<?>... Ecco come lo faccio;)


@SuppressWarnings("unchecked")viene in soccorso! (Basta essere attenti ad applicare sempre a portata come un piccolo possibile in quanto fa problemi potenziali oscuri nel codice.)
Donal Fellows

4

Seguendo la risposta di @Kire Haglin, un ulteriore esempio di metodi generici può essere visto nella documentazione di smascheramento JAXB :

public <T> T unmarshal( Class<T> docClass, InputStream inputStream )
         throws JAXBException {
  String packageName = docClass.getPackage().getName();
  JAXBContext jc = JAXBContext.newInstance( packageName );
  Unmarshaller u = jc.createUnmarshaller();
  JAXBElement<T> doc = (JAXBElement<T>)u.unmarshal( inputStream );
  return doc.getValue();
}

Ciò consente unmarshaldi restituire un documento di un tipo di albero del contenuto JAXB arbitrario.


2

Spesso si desidera utilizzare i caratteri jolly con Class. Ad esempio, Class<? extends JComponent>consentirebbe di specificare che la classe è una sottoclasse di JComponent. Se hai recuperato l' Classistanza da Class.forName, puoi usare Class.asSubclassil cast prima di tentare, per esempio, di costruire un'istanza.


2

In Java <T>significa classe generica. Una classe generica è una classe che può funzionare su qualsiasi tipo di tipo di dati o in altre parole possiamo dire che è indipendente dal tipo di dati.

public class Shape<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Dove T significa tipo. Ora quando crei l'istanza di questa classe Shape dovrai dire al compilatore per quale tipo di dati funzionerà.

Esempio:

Shape<Integer> s1 = new Shape();
Shape<String> s2 = new Shape();

Intero è un tipo e anche String è un tipo.

<T>in particolare sta per tipo generico. Secondo Java Docs: un tipo generico è una classe o un'interfaccia generica parametrizzata sui tipi.


0

Giusto per aggiungere un altro esempio, la versione generica di Class ( Class<T>) consente di scrivere funzioni generiche come quella qui sotto.

public static <T extends Enum<T>>Optional<T> optionalFromString(
        @NotNull Class<T> clazz,
        String name
) {
    return Optional<T> opt = Optional.ofNullable(name)
            .map(String::trim)
            .filter(StringUtils::isNotBlank)
            .map(String::toUpperCase)
            .flatMap(n -> {
                try {
                    return Optional.of(Enum.valueOf(clazz, n));
                } catch (Exception e) {
                    return Optional.empty();
                }
            });
}

-2

All'inizio è confuso. Ma aiuta nelle situazioni seguenti:

class SomeAction implements Action {
}

// Later in the code.
Class<Action> actionClass = Class.forName("SomeAction"); 
Action action = actionClass.newInstance();
// Notice you get an Action instance, there was no need to cast.

4
Non è solo un modo incredibilmente complicato per dire Action a = new Action ()?
Karl,

1
Action a = new Action() ? Actionho un'interfaccia, è SomeActionche stiamo cercando di ottenere un'istanza di. Abbiamo solo il nome di SomeActiondisponibile in fase di esecuzione.
fastcodejava,

Questo non passa il controllo dei caratteri: il compilatore java non ha modo di dire che <code> Class.forName ("SomeAction") </code> sarà di tipo <code>Class<Action> </code>, poiché questo sarà solo essere conosciuto in fase di esecuzione.
tonio,

@tonio, giusto, quindi probabilmente devi concludere quella prima riga in una specie di try / catch. Ma supponendo che non venga generata alcuna eccezione, la seconda riga funzionerà.
MatrixFrog,

2
Quello che stai veramente descrivendo è che SomeAction.class corrisponde al modello Class <? extends Action> - ovvero, se si dispone di un metodo useAction (Class <? estende Action> klass), è possibile chiamare useAction (SomeAction.class).
Thomas Andrews,

-5

Basta usare la classe di manzo:

public <T> T beefmarshal( Class<beef> beefClass, InputBeef inputBeef )
     throws JAXBException {
     String packageName = docClass.getPackage().getBeef();
     JAXBContext beef = JAXBContext.newInstance( packageName );
     Unmarshaller u = beef.createBeef();
     JAXBElement<T> doc = (JAXBElement<T>)u.beefmarshal( inputBeef );
     return doc.getBeef();
}

4
Questo in realtà non fornisce una risposta completa alla domanda. Se pensi di avere qualcosa da aggiungere, modifica questa risposta.
MasterAM
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.