web-dev-qa-db-ja.com

JavaでClass <T>を使うにはどうすればいいですか?

この質問 の上で、Genericsとその裏で実際に何をしているかについての十分な議論があります、それで我々は皆Vector<int[]>が整数配列のベクトルであることを知っています、 HashTable<String, Person>はキーが文字列と値Personsであるテーブルです。しかし、私が断念するのはClass<>の使い方です。

JavaクラスClassもテンプレート名を取ることになっています(あるいはEclipseの黄色い下線で言われています)。何を入れるべきかわからない。 Classオブジェクトのすべてのポイントは、リフレクションなどの目的で、オブジェクトに関する情報が完全にはない場合です。 Classオブジェクトがどのクラスを保持するのかを指定するのはなぜですか?私は明らかに知りません、または私はClassオブジェクトを使用しません、私は特定のものを使用します。

220
Karl

Classクラスの総称バージョンを使用すると、とりわけ、次のようなものを書くことができます。

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

そして、受け取ったClassオブジェクトがCollectionを継承していることを確認でき、このクラスのインスタンスは(少なくとも)Collectionになります。

119
Yuval

任意のクラスのすべてのインスタンスは、そのクラスのクラスの同じJava.lang.Classオブジェクトを共有しています」だけです。

例えば)

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

a.getClass() == b.getClass()は真実です。

今仮定

Teacher t = new Teacher();

総称がなければ以下が可能です。

Class studentClassRef = t.getClass();

しかし、これは今間違っています..?

例)public void printStudentClassInfo(Class studentClassRef) {}Teacher.classで呼び出すことができます

これは総称を使用して回避することができます。

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

今Tは何ですか? Tは型パラメータ(型変数とも呼ばれます)です。山括弧(<>)で区切られ、クラス名の後に続きます。
Tは、クラスファイルの書き込み中に宣言された変数名(任意の名前)のような単なる記号です。後でTはで置き換えられます
初期化中の有効なクラス名(HashMap<String> map = new HashMap<String>();

例)class name<T1, T2, ..., Tn>

そのためClass<T>は特定のクラス型 'T'のクラスオブジェクトを表します。

あなたのクラスメソッドは以下のような未知の型パラメータで動作しなければならないと仮定します

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

ここでTはCarNameのようにString型として使用できます。

OR TはmodelNumberのようにInteger型として使用できます。

OR Tは有効な自動車インスタンスのようにObject型として使用できます。

今ここに上記は実行時に異なって使用することができる簡単なPOJOです。
コレクション、例えばList、Set、Hashmapは、Tの宣言に従ってさまざまなオブジェクトを扱う最良の例ですが、いったんTをStringとして宣言すると、
eg)HashMap<String> map = new HashMap<String>();そして、Stringクラスのインスタンスオブジェクトだけを受け付けます。

汎用メソッド

ジェネリックメソッドは、独自の型パラメータを導入するメソッドです。これはジェネリック型の宣言と似ていますが、型パラメータの有効範囲は宣言されているメソッドに限定されます。静的および非静的なジェネリックメソッド、およびジェネリッククラスコンストラクターが許可されています。

汎用メソッドの構文には、角括弧内に型パラメータが含まれており、メソッドの戻り型の前に表示されます。ジェネリックメソッドの場合、typeパラメータセクションはメソッドの戻り型の前になければなりません。

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

ここで<K, V, Z, Y>は、ここでbooleanである戻り型の前にあるべきであるメソッド引数で使用される型の宣言です。

以下では型宣言<T>はクラスレベルですでに宣言されているので、メソッドレベルでは必要ありません。

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

ただし、クラスレベルの型パラメータK、V、Z、およびYは静的コンテキストでは使用できないため、以下は間違っています(ここでは静的メソッド)。

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

その他の有効なシナリオは

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

そして最後に静的メソッドは常に明示的な<T>宣言を必要とします。クラスレベルのClass<T>から派生したものではありません。これは、クラスレベルTがインスタンスにバインドされているためです。

ジェネリックスに関する制限 も読んでください。

158

Javaの資料から

[...]さらに驚くべきことに、クラスClassは一般化されました。クラスリテラルは型トークンとして機能し、実行時とコンパイル時の両方の型情報を提供します。これにより、新しいAnnotatedElementインタフェースのgetAnnotationメソッドで例示されているスタイルの静的ファクトリが有効になります。

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

これは一般的な方法です。次のスニペットに示すように、引数から型パラメータTの値を推測し、適切なTのインスタンスを返します。

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

ジェネリック医薬品の前に、あなたは結果を作者にキャストしなければならなかったでしょう。また、実際のパラメータがAnnotationのサブクラスを表していることをコンパイラに確認させる方法はありませんでした。 [...]

まあ、私はこの種のものを使う必要はありませんでした。誰でも?

32
raupach

サービスレジストリの検索を作成するときにclass<T>が役に立つことがわかりました。例えば。

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

他の答えが指摘するように、このclassが一般的にされた理由はたくさんあり、それには正当な理由があります。ただし、Class<T>で使用するジェネリック型を知る方法がない場合がたくさんあります。このような場合は、Eclipseの黄色い警告を単に無視するか、Class<?>を使用することができます。

5
bruno conde

@Kire Haglinの答えに続いて、ジェネリックメソッドのさらなる例が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();
}

これはunmarshalが任意のJAXBコンテンツツリータイプのドキュメントを返すことを可能にします。

3
Stew

Java <T>では、汎用クラスを意味します。ジェネリッククラスは、あらゆるタイプのデータタイプで動作できるクラスです。つまり、データタイプに依存しないと言えます。

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

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

ここで、Tはタイプを意味します。これで、このShapeクラスのインスタンスを作成するときに、コンパイラがどのデータ型で作業するかを伝える必要があります。

例:

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

整数はタイプであり、文字列もタイプです。

<T>は、特にジェネリック型を表します。 Java Docsによると-ジェネリック型は、ジェネリッククラスまたはインターフェイスで、パラメータ化オーバータイプです。

2
Priyesh Diukar

ワイルドカードをClassと一緒に使用したいことがよくあります。たとえば、Class<? extends JComponent>では、クラスがJComponentのサブクラスであることを指定できます。 Class.forNameからClassインスタンスを取得した場合は、インスタンスの構築などを試みる前にClass.asSubclassを使用してキャストを実行できます。

別の例を挙げると、ジェネリック版のClass(Class<T>)では以下のようなジェネリック関数を書くことができます。

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