私が持っています;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
リストのジェネリック型を取得する(簡単な)方法はありますか?
私が持っています;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
リストのジェネリック型を取得する(簡単な)方法はありますか?
回答:
それらが実際に特定のクラスのフィールドである場合は、リフレクションを少し使ってそれらを取得できます。
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.
}
}
また、パラメーターの型とメソッドの戻り値の型についても行うことができます。
しかし、それらがクラス/メソッドの同じスコープ内にあり、それらについて知る必要がある場合は、すでに自分で宣言しているため、それらを知る意味はありません。
<?>
ではなく(ワイルドカードタイプ)を使用してい<Class>
ます。あなた交換する<?>
ことにより、<Class>
または何を<E>
。
メソッドパラメータについても同じことができます。
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
短い答え:いいえ。
これはおそらく重複です。現在適切なものを見つけることができません。
Javaは型消去と呼ばれるものを使用します。これは、実行時に両方のオブジェクトが同等であることを意味します。コンパイラは、リストに整数または文字列が含まれていることを認識しているため、タイプセーフな環境を維持できます。この情報は実行時に(オブジェクトインスタンスベースで)失われ、リストには「オブジェクト」のみが含まれます。
あなたはクラスについて少し知ることができます、それがどのタイプによってパラメータ化されるかもしれませんが、通常これは単に「オブジェクト」を拡張するもの、すなわち何でもです。タイプを次のように定義した場合
class <A extends MyClass> AClass {....}
AClass.classには、パラメーターAがMyClassによってバインドされているという事実のみが含まれますが、それ以上のことを伝える方法はありません。
List<Integer>
and として明示的に宣言していますList<String>
。これらの場合、型消去は適用されません。
コレクションのジェネリック型は、実際にオブジェクトが含まれている場合にのみ問題になるはずですよね?だから、それを行うのは簡単ではありませんか?
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());
}
Object
sのリストから引き出すときに、それらを型に基づいて適切なハンドラーに渡します。
返された型のジェネリック型を取得する必要がある場合は、aを返したクラス内のメソッドを見つけて、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クラスです
スティーブ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);
}
}
実行時には、できません。
ただし、リフレクションを介して型パラメーターにアクセスできます。試す
for(Field field : this.getDeclaredFields()) {
System.out.println(field.getGenericType())
}
このメソッドgetGenericType()
はTypeオブジェクトを返します。この場合、それはのインスタンスになりParametrizedType
、これにはメソッドgetRawType()
(List.class
この場合はを含む)とgetActualTypeArguments()
、配列(この場合、長さ1の、String.class
またはを含むInteger.class
)を返します。
method.getGenericParameterTypes()
して、メソッドのパラメータの宣言された型を取得できます。
同じ問題がありましたが、代わりに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");
}
}
}
これには、チェックされていないキャストの使用が含まれるため、リストであることがわかっている場合、およびそれがどのタイプであるかを知っている場合にのみ、これを実行してください。
他の人が言ったように、唯一の正しい答えは「いいえ」で、タイプは消去されました。
リストにゼロ以外の数の要素がある場合、最初の要素のタイプを調べることができます(たとえば、getClassメソッドを使用して)。リストのジェネリック型はわかりませんが、ジェネリック型はリスト内の型のスーパークラスであると想定するのが妥当です。
私はそのアプローチを主張しませんが、束縛の中でそれは役に立つかもしれません。
List<Integer>
and として明示的に宣言していますList<String>
。これらの場合、型消去は適用されません。
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();
}
}
getFieldGenericTypefieldGenericType
それが見つからないものは何
タイプは消去されるため、消去することはできません。http://en.wikipedia.org/wiki/Type_erasureおよびhttp://en.wikipedia.org/wiki/Generics_in_Java#Type_erasureを参照してください
List<Integer>
and として明示的に宣言していますList<String>
。これらの場合、型消去は適用されません。
リフレクションを使用しField
てこれらを取得し、次に行うことができfield.genericType
ます。ジェネリックに関する情報を含む型も取得します。