나는 가지고있다;
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.
}
}
매개 변수 유형 및 리턴 유형 메소드에 대해서도이를 수행 할 수 있습니다.
그러나 그들이 당신이 그들에 대해 알아야 할 클래스 / 방법의 동일한 범위 내에 있다면, 당신이 이미 스스로 선언했기 때문에 그것을 알 필요가 없습니다.
답변
메소드 매개 변수에 대해서도 동일한 작업을 수행 할 수 있습니다.
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는 유형 삭제라는 것을 사용합니다. 즉, 런타임시 두 객체가 동일합니다. 컴파일러는 목록에 정수 또는 문자열이 포함되어 있으므로 형식이 안전한 환경을 유지할 수 있다는 것을 알고 있습니다. 이 정보는 런타임에 (객체 인스턴스별로) 손실되며 목록에는 ‘개체’만 포함됩니다.
클래스에 대해 조금, 매개 변수화 할 수있는 유형을 알 수 있지만 일반적으로 이것은 “Object”를 확장하는 것, 즉 무엇이든입니다. 유형을 다음과 같이 정의하면
class <A extends MyClass> AClass {....}
AClass.class는 매개 변수 A가 MyClass에 의해 제한된다는 사실 만 포함하지만 그 이상으로 말할 방법이 없습니다.
답변
컬렉션의 제네릭 형식은 실제로 개체가 들어있는 경우에만 중요합니다. 따라서 수행하기가 쉽지 않습니다.
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());
}
답변
한 필드의 일반 유형을 찾는 경우 :
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
답변
제네릭 형식의 반환 형식을 가져와야하는 경우 클래스에서 메서드를 찾아서 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입니다.
답변
Steve 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);
}
}