web-dev-qa-db-de.com

Wie benutze ich die Klasse <T> in Java?

Es gibt eine gute Diskussion über Generics und was sie hinter den Kulissen von this question wirklich tun, also wissen wir alle, dass Vector<int[]> ein Vektor von ganzzahligen Arrays ist und HashTable<String, Person> eine Tabelle, deren Schlüssel Strings und Werte Persons sind. Was mich jedoch stumpft, ist die Verwendung von Class<>.

Die Java-Klasse Class soll auch einen Vorlagennamen annehmen (oder so wird mir die gelbe Unterstreichung in Eclipse mitgeteilt). Ich verstehe nicht, was ich dort einsetzen sollte. Der springende Punkt des Class-Objekts liegt darin, dass Sie die Informationen zu einem Objekt, zur Reflexion und dergleichen nicht vollständig haben. Warum muss ich angeben, welche Klasse das Class-Objekt enthalten soll? Ich weiß es natürlich nicht, oder ich würde das Class-Objekt nicht verwenden, ich würde das spezifische Objekt verwenden.

206
Karl

Mit der generierten Version von Class können Sie unter anderem Dinge wie schreiben

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

dann können Sie sicher sein, dass das Klassenobjekt, das Sie erhalten, Collection erweitert, und eine Instanz dieser Klasse (mindestens) eine Collection ist.

115
Yuval

Alles, was wir wissen, ist "Alle Instanzen einer beliebigen Klasse teilen sich das gleiche Java.lang.Class-Objekt dieses Klassentyps

z.B) 

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

Dann ist a.getClass() == b.getClass() wahr.

Nun nehmen wir an

Teacher t = new Teacher();

ohne Generika ist das Folgende möglich.

Class studentClassRef = t.getClass();

Aber das ist jetzt falsch ..?

beispiel: public void printStudentClassInfo(Class studentClassRef) {} kann mit Teacher.class aufgerufen werden 

Dies kann mit Generika vermieden werden.

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

Was ist jetzt T ?? T ist Typparameter (auch Typvariablen genannt); durch spitze Klammern (<>) getrennt, folgt der Klassenname.
T ist nur ein Symbol, wie ein Variablenname (kann ein beliebiger Name sein), der beim Schreiben der Klassendatei deklariert wird. Später wird dieses T durch ersetzt 
gültiger Klassenname während der Initialisierung (HashMap<String> map = new HashMap<String>();)

z. B. class name<T1, T2, ..., Tn>

Class<T> repräsentiert also ein Klassenobjekt eines bestimmten Klassentyps 'T'.

Angenommen, Ihre Klassenmethoden müssen mit unbekannten Typparametern wie unten arbeiten

/**
 * 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; }
}

Hier kann T als String Typ als CarName verwendet werden.

ODER T kann als Integer Typ als modelNumber verwendet werden,

ODER T kann als Object-Typ als gültige Fahrzeuginstanz verwendet werden. 

Jetzt ist hier das einfache POJO, das zur Laufzeit anders verwendet werden kann.
Sammlungen, z. B. List, Set, Hashmap, sind beste Beispiele, die mit verschiedenen Objekten gemäß der Deklaration von T arbeiten, aber sobald T als String .__ deklariert wurde.
beispiel) HashMap<String> map = new HashMap<String>(); Dann werden nur Instanzobjekte von String-Klassen akzeptiert.

Generische Methoden

Generische Methoden sind Methoden, die ihre eigenen Typparameter einführen. Dies ist vergleichbar mit der Deklaration eines generischen Typs, der Gültigkeitsbereich des Typparameters ist jedoch auf die Methode beschränkt, in der er deklariert wird. Statische und nicht statische generische Methoden sowie generische Klassenkonstruktoren sind zulässig.

Die Syntax für eine generische Methode enthält einen Typparameter in spitzen Klammern und wird vor dem Rückgabetyp der Methode angezeigt. Bei generischen Methoden muss der Typparameterabschnitt vor dem Rückgabetyp der Methode angezeigt werden.

 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;
}

Hier ist <K, V, Z, Y> die Deklaration der in den Methodenargumenten verwendeten Typen, die vor dem Rückgabetyp stehen sollten, der hier boolean ist.

Im unten; Die Typdeklaration <T> ist auf Methodenebene nicht erforderlich, da sie bereits auf Klassenebene deklariert ist.

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

Unten ist jedoch falsch, da die Parameter der Klassenebene K, V, Z und Y nicht in einem statischen Kontext verwendet werden können (statische Methode hier).

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());
    }
}

ANDERE GÜLTIGE SZENARIEN SIND

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;
        }
    }

Und schließlich benötigt die statische Methode immer eine explizite <T>-Deklaration. Es wird nicht von der Klassenebene Class<T> abgeleitet. Dies liegt daran, dass die Klassenstufe T an die Instanz gebunden ist. 

Lesen Sie auch Einschränkungen für Generics

148

Aus der Java-Dokumentation:

[...] Überraschenderweise wurde Klasse Class generiert. Klassenliterale fungieren jetzt als Typ-Token und bieten sowohl Informationen zur Laufzeit als auch zur Kompilierzeit. Dies ermöglicht einen Stil statischer Factories, die durch die getAnnotation-Methode in der neuen AnnotatedElement-Schnittstelle veranschaulicht werden:

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

Dies ist eine generische Methode. Es holt den Wert seines Typparameters T aus seinem Argument und gibt eine entsprechende Instanz von T zurück, wie durch den folgenden Ausschnitt veranschaulicht:

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

Vor Generika hätten Sie das Ergebnis an Author übertragen müssen. Sie hätten auch keine Möglichkeit haben, den Compiler zu überprüfen, ob der Aktualparameter eine Unterklasse von Annotation darstellt. [...]

Nun, ich musste nie so etwas benutzen. Jemand?

30
raupach

Ich habe class<T> als nützlich erachtet, wenn ich Suchvorgänge für die Dienstregistrierungsdatenbank erstelle. Z.B.

<T> T getService(Class<T> serviceClass)
{
    ...
}
8
Kire Haglin

Wie andere Antworten zeigen, gibt es viele und gute Gründe, warum diese class generisch gemacht wurde. Es gibt jedoch viele Fälle, in denen Sie den generischen Typ, der mit Class<T> verwendet werden soll, nicht kennen. In diesen Fällen können Sie einfach die gelben Eclipse-Warnungen ignorieren oder Class<?> verwenden. So mache ich das;)

5
bruno conde

Nach der Antwort von @Kire Haglin kann ein weiteres Beispiel für Generikamethoden in der Dokumentation für JAXB unmarshalling gesehen werden:

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();
}

Dadurch kann unmarshal ein Dokument eines beliebigen JAXB-Inhaltsbaumtyps zurückgeben.

3
Stew

In Java <T> bedeutet generische Klasse. Eine generische Klasse ist eine Klasse, die mit jeder Art von Datentyp arbeiten kann, oder mit anderen Worten, sie ist unabhängig vom Datentyp.

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

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

Wobei T Typ bedeutet. Wenn Sie nun eine Instanz dieser Shape-Klasse erstellen, müssen Sie dem Compiler mitteilen, für welchen Datentyp dies ausgeführt werden soll.

Beispiel:

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

Integer ist ein Typ und String ist ebenfalls ein Typ.

<T> steht speziell für generischen Typ. Gemäß Java Docs - Ein generischer Typ ist eine generische Klasse oder Schnittstelle, die parametrisiert über Typen ist.

2
Priyesh Diukar

Sie möchten häufig Platzhalter mit Class verwenden. Mit Class<? extends JComponent> können Sie beispielsweise angeben, dass die Klasse eine Unterklasse von JComponent ist. Wenn Sie die Class-Instanz von Class.forName abgerufen haben, können Sie mit Class.asSubclass die Umwandlung durchführen, bevor Sie beispielsweise versuchen, eine Instanz zu erstellen.

Um nur in einem anderen Beispiel zu werfen, können Sie mit der generischen Version von Class (Class<T>) generische Funktionen wie die folgende schreiben.

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();
                }
            });
}
0
zeronone