web-dev-qa-db-ja.com

Java.util.Listのジェネリック型を取得します。

私は持っています;

List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();

リストのジェネリック型を取得する(簡単な)方法はありますか?

231
Thys

これらが実際には特定のクラスのフィールドである場合は、リフレクションを少し使用するとそれらを取得できます。

package test;

import Java.lang.reflect.Field;
import Java.lang.reflect.ParameterizedType;
import Java.util.ArrayList;
import Java.util.List;

public class Test {

    List<String> stringList = new ArrayList<String>();
    List<Integer> integerList = new ArrayList<Integer>();

    public static void main(String... args) throws Exception {
        Field stringListField = Test.class.getDeclaredField("stringList");
        ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
        Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
        System.out.println(stringListClass); // class Java.lang.String.

        Field integerListField = Test.class.getDeclaredField("integerList");
        ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
        Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
        System.out.println(integerListClass); // class Java.lang.Integer.
    }
}

パラメータの型や戻り値のメソッドの型に対してもこれを行うことができます。

しかし、それらがあなたがそれらについて知る必要があるクラス/メソッドの同じ範囲内にあるならば、それらを知ることの意味がありません。

371
BalusC

メソッドパラメータについても同じことができます。

Type[] types = method.getGenericParameterTypes();
//Now assuming that the first parameter to the method is of type List<Integer>
ParameterizedType pType = (ParameterizedType) types[0];
Class<?> clazz = (Class<?>) pType.getActualTypeArguments()[0];
System.out.println(clazz); //prints out Java.lang.Integer
17
tsaixingwei

短い答え:いいえ。

これはおそらく重複しているので、今は適切なものを見つけることができません。

Javaは型消去と呼ばれるものを使用します。つまり、実行時には両方のオブジェクトは同等です。コンパイラは、リストに整数または文字列が含まれていることを認識しているため、型安全な環境を維持できます。この情報は実行時に(オブジェクトインスタンス単位で)失われ、リストには 'Objects'のみが含まれます。

クラス、それがどの型によってパラメータ化される可能性があるかについて少し知ることができますが、通常これは単なる "Object"を拡張するもの、つまりすべてです。型を次のように定義すると

class <A extends MyClass> AClass {....}

AClass.classには、パラメータAがMyClassによって制限されているという事実だけが含まれますが、それ以上のことはわかりません。

17
falstro

返された型のジェネリック型を取得する必要がある場合は、Collectionを返すクラスでメソッドを見つけてそのジェネリック型にアクセスする必要があるときに、この方法を使用しました。

import Java.lang.reflect.Method;
import Java.lang.reflect.ParameterizedType;
import Java.lang.reflect.Type;
import Java.util.Collection;
import Java.util.List;

public class Test {

    public List<String> test() {
        return null;
    }

    public static void main(String[] args) throws Exception {

        for (Method method : Test.class.getMethods()) {
            Class returnClass = method.getReturnType();
            if (Collection.class.isAssignableFrom(returnClass)) {
                Type returnType = method.getGenericReturnType();
                if (returnType instanceof ParameterizedType) {
                    ParameterizedType paramType = (ParameterizedType) returnType;
                    Type[] argTypes = paramType.getActualTypeArguments();
                    if (argTypes.length > 0) {
                        System.out.println("Generic type is " + argTypes[0]);
                    }
                }
            }
        }

    }

}

これは出力します:

ジェネリック型はクラスJava.lang.Stringです。

9
Aram Kocharyan

1つのフィールドのジェネリック型を見つけるために:

((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
9
Mohsen Kashi

コレクションのジェネリック型は、実際にオブジェクトが含まれている場合にのみ問題になります。だから、そうすることが簡単ではないです:

Collection<?> myCollection = getUnknownCollectionFromSomewhere();
Class genericClass = null;
Iterator it = myCollection.iterator();
if (it.hasNext()){
    genericClass = it.next().getClass();
}
if (genericClass != null) { //do whatever we needed to know the type for

実行時にジェネリック型のようなものはありませんが、実行時に内部のオブジェクトは宣言されたジェネリックと同じ型であることが保証されているので、処理する前にアイテムのクラスをテストするだけで十分簡単です。

他にできることは、リストを処理して適切なタイプのメンバーを取得し、他のメンバーを無視する(またはそれらを異なる方法で処理する)ことです。

Map<Class<?>, List<Object>> classObjectMap = myCollection.stream()
    .filter(Objects::nonNull)
    .collect(Collectors.groupingBy(Object::getClass));

// Process the list of the correct class, and/or handle objects of incorrect
// class (throw exceptions, etc). You may need to group subclasses by
// filtering the keys. For instance:

List<Number> numbers = classObjectMap.entrySet().stream()
        .filter(e->Number.class.isAssignableFrom(e.getKey()))
        .flatMap(e->e.getValue().stream())
        .map(Number.class::cast)
        .collect(Collectors.toList());

これにより、クラスがNumberのサブクラスであったすべての項目のリストが得られ、これを必要に応じて処理することができます。残りの項目は他のリストに除外されました。それらはマップ内にあるため、必要に応じて処理することも、無視することもできます。

他のクラスの項目をすべて無視したい場合は、はるかに簡単になります。

List<Number> numbers = myCollection.stream()
    .filter(Number.class::isInstance)
    .map(Number.class::cast)
    .collect(Collectors.toList());

リストに特定のクラスに一致するアイテムのみが含まれるようにするためのユーティリティメソッドを作成することもできます。

public <V> List<V> getTypeSafeItemList(Collection<Object> input, Class<V> cls) {
    return input.stream()
            .filter(cls::isInstance)
            .map(cls::cast)
            .collect(Collectors.toList());
}
8
Steve K

スティーブKの答えを拡大する:

/** 
* Performs a forced cast.  
* Returns null if the collection type does not match the items in the list.
* @param data The list to cast.
* @param listType The type of list to cast to.
*/
static <T> List<? super T> castListSafe(List<?> data, Class<T> listType){
    List<T> retval = null;
    //This test could be skipped if you trust the callers, but it wouldn't be safe then.
    if(data!=null && !data.isEmpty() && listType.isInstance(data.iterator().next().getClass())) {
        @SuppressWarnings("unchecked")//It's OK, we know List<T> contains the expected type.
        List<T> foo = (List<T>)data;
        return retval;
    }
    return retval;
}
Usage:

protected WhateverClass add(List<?> data) {//For fluant useage
    if(data==null) || data.isEmpty(){
       throw new IllegalArgumentException("add() " + data==null?"null":"empty" 
       + " collection");
    }
    Class<?> colType = data.iterator().next().getClass();//Something
    aMethod(castListSafe(data, colType));
}

aMethod(List<Foo> foo){
   for(Foo foo: List){
      System.out.println(Foo);
   }
}

aMethod(List<Bar> bar){
   for(Bar bar: List){
      System.out.println(Bar);
   }
}
6
ggb667

同じ問題がありましたが、代わりにinstanceofを使用しました。このようにしました:

List<Object> listCheck = (List<Object>)(Object) stringList;
    if (!listCheck.isEmpty()) {
       if (listCheck.get(0) instanceof String) {
           System.out.println("List type is String");
       }
       if (listCheck.get(0) instanceof Integer) {
           System.out.println("List type is Integer");
       }
    }
}

これはチェックされていないキャストを使用することを含むので、あなたがそれがリストであること、そしてそれがどんなタイプであることができるかを知っているときだけこれをしてください。

4
Andy

実行時には、いいえ、できません。

しかしリフレクションを介して型パラメータareアクセス可能です。やってみる

for(Field field : this.getDeclaredFields()) {
    System.out.println(field.getGenericType())
}

メソッドgetGenericType()はTypeオブジェクトを返します。この場合、それはParametrizedTypeのインスタンスになります。これには、メソッドgetRawType()(この場合はList.classが入ります)およびgetActualTypeArguments()が(この場合はString.classまたはInteger.classのいずれかを含む長さ1の)戻ります。 ).

4
Scott Morrison

List<String>List<Integer>は同じランタイムクラスを共有しているので、一般的に不可能です。

ただし、リストを保持しているフィールドの宣言された型を反映させることができます(宣言された型自体が値を知らない型パラメータを参照していない場合)。

3
meriton

他の人が言っているように、唯一の正しい答えはノーです、タイプは消去されました。

リストにゼロ以外の数の要素がある場合は、最初の要素の型を調べることができます(たとえば、getClassメソッドを使用して)。これでリストのジェネリック型はわかりませんが、ジェネリック型はリスト内の型のスーパークラスであると想定するのが妥当でしょう。

私はそのアプローチを主張しないでしょう、しかし束縛でそれは役に立つかもしれません。

2
Chris Arguin
import org.junit.Assert;
import org.junit.Test;

import Java.lang.reflect.Field;
import Java.lang.reflect.ParameterizedType;
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.List;

public class GenericTypeOfCollectionTest {
    public class FormBean {
    }

    public class MyClazz {
        private List<FormBean> list = new ArrayList<FormBean>();
    }

    @Test
    public void testName() throws Exception {
        Field[] fields = MyClazz.class.getFields();
        for (Field field : fields) {
            //1. Check if field is of Collection Type
            if (Collection.class.isAssignableFrom(field.getType())) {
                //2. Get Generic type of your field
                Class fieldGenericType = getFieldGenericType(field);
                //3. Compare with <FromBean>
                Assert.assertTrue("List<FormBean>",
                  FormBean.class.isAssignableFrom(fieldGenericType));
            }
        }
    }

    //Returns generic type of any field
    public Class getFieldGenericType(Field field) {
        if (ParameterizedType.class.isAssignableFrom(field.getGenericType().getClass())) {
            ParameterizedType genericType =
             (ParameterizedType) field.getGenericType();
            return ((Class)
              (genericType.getActualTypeArguments()[0])).getSuperclass();
        }
        //Returns dummy Boolean Class to compare with ValueObject & FormBean
        return new Boolean(false).getClass();
    }
}
1
Ashish

Reflectionを使用してこれらのFieldを取得すると、次のように実行できます。field.genericTypeはgenericに関する情報も含む型を取得するためのものです。

あなたがすることができなくなりますのでタイプは消去されます。 http://en.wikipedia.org/wiki/Type_erasure および http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure を参照してください。

0