Wie verwende ich die Klasse <T> in Java?

247

Es gibt eine gute Diskussion über Generika und was sie wirklich hinter den Kulissen bei dieser Frage tun. Wir alle wissen, dass dies Vector<int[]>ein Vektor von ganzzahligen Arrays ist und HashTable<String, Person>eine Tabelle, deren Schlüssel Zeichenfolgen und Werte sind Person. Was mich jedoch verblüfft, ist die Verwendung von Class<>.

Die Java-Klasse Classsoll auch einen Vorlagennamen annehmen (oder so wird mir die gelbe Unterstreichung in Eclipse mitgeteilt). Ich verstehe nicht, was ich da reinstellen soll. Der springende Punkt des ClassObjekts ist, wenn Sie nicht vollständig über die Informationen zu einem Objekt verfügen, um darüber nachzudenken und so weiter. Warum muss ich angeben, welche Klasse das ClassObjekt enthalten soll? Ich weiß es eindeutig nicht, oder ich würde das ClassObjekt nicht verwenden, ich würde das spezifische verwenden.

Karl
quelle

Antworten:

136

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

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

und dann können Sie sicher sein, dass das empfangene CollectionKlassenobjekt erweitert wird und eine Instanz dieser Klasse (mindestens) eine Sammlung ist.

Yuval
quelle
183

Wir wissen nur: " Alle Instanzen einer Klasse haben dasselbe java.lang.Class-Objekt dieses Klassentyps. "

z.B)

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

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

Nehmen wir nun an

Teacher t = new Teacher();

Ohne Generika ist das Folgende möglich.

Class studentClassRef = t.getClass();

Aber das ist jetzt falsch ..?

zB) public void printStudentClassInfo(Class studentClassRef) {}kann mit aufgerufen werdenTeacher.class

Dies kann mit Generika vermieden werden.

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

Was ist nun T? T sind Typparameter (auch Typvariablen genannt); wird durch spitze Klammern (<>) begrenzt und folgt dem Klassennamen.
T ist nur ein Symbol, wie ein Variablenname (kann ein beliebiger Name sein), der beim Schreiben der Klassendatei deklariert wird. Später wird T
während der Initialisierung durch einen gültigen Klassennamen ersetzt ( HashMap<String> map = new HashMap<String>();)

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

Stellt also Class<T>ein Klassenobjekt des spezifischen Klassentyps ' T' dar.

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 StringTyp als CarName verwendet werden

OR T kann als IntegerTyp als modelNumber verwendet werden.

OR T kann als ObjectTyp als gültige Fahrzeuginstanz verwendet werden .

Hier ist das einfache POJO, das zur Laufzeit unterschiedlich verwendet werden kann.
Sammlungen, z. B.) List, Set, Hashmap, sind die besten Beispiele, die mit verschiedenen Objekten gemäß der Deklaration von T funktionieren. Wenn wir jedoch T als String deklariert haben,
z. B.) HashMap<String> map = new HashMap<String>();werden nur Instanzobjekte der String-Klasse akzeptiert.

Generische Methoden

Generische Methoden sind Methoden, die ihre eigenen Typparameter einführen. Dies ähnelt der Deklaration eines generischen Typs, der Gültigkeitsbereich des Typparameters ist jedoch auf die Methode beschränkt, bei 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 <K, V, Z, Y>ist die Deklaration der Typen, die in den Methodenargumenten verwendet werden, die vor dem hier angegebenen Rückgabetyp stehen sollten boolean.

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

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

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

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 muss die statische Methode immer explizit <T>deklariert werden. Es wird nicht von der Klassenstufe abgeleitet Class<T>. Dies liegt daran, dass die Klassenstufe T an die Instanz gebunden ist.

Lesen Sie auch Einschränkungen für Generika

Platzhalter und Subtypisierung

Typargument für eine generische Methode

Kanagavelu Sugumar
quelle
2
Meine Antwort auf begrenzte WildCards stackoverflow.com/questions/1368166/…
Kanagavelu Sugumar
"Klasse <T> repräsentiert ein Klassenobjekt des bestimmten Klassentyps 'T'." Das ist sinnvoll. Vielen Dank ..
bynu022
Diese Antwort ist furchtbar verwirrend in der Art und Weise, wie Klassen (aus der Schule) in einer Frage zu Klassen (in Java) verwendet werden. Es ist schwer zu wissen, wovon der Autor von einem Satz zum anderen spricht.
Echox
@Echox Es tut mir leid, ich kann es verbessern, wenn Sie spezielle Fragen haben.
Kanagavelu Sugumar
32

Aus der Java-Dokumentation:

[...] Überraschenderweise wurde die Klassenklasse generiert. Klassenliterale fungieren jetzt als Typ-Token und bieten Informationen zur Laufzeit und zur Kompilierungszeit. Dies ermöglicht einen Stil statischer Fabriken, der durch die Methode getAnnotation in der neuen AnnotatedElement-Schnittstelle veranschaulicht wird:

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

Dies ist eine generische Methode. Es leitet den Wert seines Typparameters T aus seinem Argument ab und gibt eine geeignete Instanz von T zurück, wie im folgenden Snippet dargestellt:

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

Vor Generika hätten Sie das Ergebnis an Author übertragen müssen. Außerdem hätten Sie den Compiler nicht überprüfen lassen können, ob der tatsächliche Parameter eine Unterklasse von Annotation darstellt. [...]

Nun, ich musste so etwas nie benutzen. Jemand?

Raupach
quelle
2
Ich dachte ich hätte. Für eine Art Framework, mit dem ich gearbeitet habe, mussten Sie den Klassennamen der Dienste übergeben, von denen Ihr Modul abhängig war. Darüber habe ich eine Ebene erstellt, die Klassenobjekte verwendet, um die Auswahl zu beschränken. Mit der Class<? extends X>Notation, die ich dachte, könnte ich sie nur auf "Dienst" -Typen beschränken. Außer es gab keinen gemeinsamen "Service" -Typ, also konnte ich es nur mit tun Class<?>. Ach.
Fwielstra
9

Ich habe es class<T>nützlich gefunden , wenn ich Service Registry Lookups erstelle. Z.B

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

Wie andere Antworten zeigen, gibt es viele und gute Gründe, warum dies classgenerisch gemacht wurde. Es gibt jedoch viele Fälle, in denen Sie nicht wissen, mit welchem ​​generischen Typ Sie arbeiten sollen Class<T>. In diesen Fällen können Sie die gelben Eclipse-Warnungen einfach ignorieren oder verwenden Class<?>... So mache ich das;)

bruno conde
quelle
@SuppressWarnings("unchecked")kommt zur Rettung! (Nur vorsichtig sein , immer es wie möglich so klein einen Umfang anzuwenden , wie es tut obskure mögliche Probleme in Ihrem Code.)
Donal Fellows
4

Nach der Antwort von @Kire Haglin finden Sie ein weiteres Beispiel für generische Methoden in der Dokumentation zum JAXB-Unmarshalling :

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

Auf diese Weise können Sie unmarshalein Dokument eines beliebigen JAXB-Inhaltsbaumtyps zurückgeben.

Eintopf
quelle
2

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

Tom Hawtin - Tackline
quelle
2

In Java <T>bedeutet generische Klasse. Eine generische Klasse ist eine Klasse, die mit jedem Datentyp arbeiten kann, oder mit anderen Worten, wir können sagen, dass sie vom Datentyp unabhängig ist.

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 arbeiten wird.

Beispiel:

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

Ganzzahl ist ein Typ und String ist auch ein Typ.

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

Frostbyte1010
quelle
0

Um nur ein weiteres Beispiel zu nennen: Mit der generischen Version von Class ( Class<T>) können generische Funktionen wie die folgende geschrieben werden.

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

Am Anfang ist es verwirrend. Aber es hilft in den folgenden Situationen:

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.
fastcodejava
quelle
4
Ist das nicht nur eine unglaublich komplizierte Art, Aktion a = neue Aktion () zu sagen?
Karl
1
Action a = new Action() ? ActionIn einer Schnittstelle SomeActionversuchen wir, eine Instanz von zu bekommen. Wir haben nur den Namen SomeActionzur Laufzeit zur Verfügung.
Fastcodejava
Dies besteht keine Typüberprüfung: Der Java-Compiler kann nicht feststellen, dass <code> Class.forName ("SomeAction") </ code> vom Typ <code> Class <Action> </ code> ist, da dies nur der Fall ist zur Laufzeit bekannt sein.
Tonio
@tonio, richtig, also musst du diese erste Zeile wahrscheinlich in eine Art Versuch / Fang einschließen. Unter der Annahme, dass dort keine Ausnahme ausgelöst wird, funktioniert die zweite Zeile garantiert.
MatrixFrog
2
Was Sie wirklich beschreiben, ist, dass SomeAction.class mit dem Muster Class <übereinstimmt? Erweitert Aktion> - Wenn Sie also eine Methode useAction (Klasse <? erweitert Aktion> klass) haben, können Sie useAction (SomeAction.class) aufrufen.
Thomas Andrews
-5

Verwenden Sie einfach die Rindfleischklasse:

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();
}
yaa
quelle
4
Dies liefert keine vollständige Antwort auf die Frage. Wenn Sie glauben, etwas hinzuzufügen, bearbeiten Sie diese Antwort.
MasterAM