IT story

일반적인 유형의 java.util.List 가져 오기

hot-time 2020. 4. 9. 08:10
반응형

일반적인 유형의 java.util.List 가져 오기


나는 가지고있다;

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

매개 변수 유형 및 리턴 유형 메소드에 대해서도이를 수행 할 수 있습니다.

그러나 그들이 당신이 그들에 대해 알아야 할 클래스 / 방법의 동일한 범위 내에 있다면, 당신이 이미 스스로 선언했기 때문에 그것을 알 필요가 없습니다.


짧은 대답 : 아닙니다.

이것은 아마도 중복 일 것입니다. 지금은 적절한 것을 찾을 수 없습니다.

Java는 유형 삭제라는 것을 사용합니다. 즉, 런타임시 두 객체가 동일합니다. 컴파일러는 목록에 정수 나 문자열이 포함되어 있으므로 형식이 안전한 환경을 유지할 수 있다는 것을 알고 있습니다. 이 정보는 런타임에 (객체 인스턴스별로) 손실되며 목록에는 '개체'만 포함됩니다.

클래스에 대해 조금, 매개 변수화 할 수있는 유형을 알 수 있지만 일반적으로 이것은 "Object"를 확장하는 것, 즉 무엇이든입니다. 유형을 다음과 같이 정의하면

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

AClass.class는 매개 변수 A가 MyClass에 의해 제한된다는 사실 만 포함하지만 그 이상으로 말할 방법이 없습니다.


메소드 매개 변수에 대해서도 동일한 작업을 수행 할 수 있습니다.

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

한 필드의 일반 유형을 찾는 경우 :

((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입니다.


컬렉션의 제네릭 형식은 실제로 개체가 들어있는 경우에만 중요합니다. 따라서 수행하기가 쉽지 않습니다.

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

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

런타임시에는 불가능합니다.

그러나 리플렉션을 통해 유형 매개 변수 액세스 할 수 있습니다. 시험

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

이 메소드 getGenericType()는 Type 객체를 반환합니다. 이 경우,의 인스턴스가되며 ParametrizedType,이 메소드에는 메소드 getRawType()( List.class이 경우 에는 포함 )와 getActualTypeArguments()배열 (이 경우에는 길이가 1 String.class또는 하나 포함 Integer.class) 을 리턴합니다 .


같은 문제가 있었지만 대신 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");
       }
    }
}

이것은 확인되지 않은 캐스트를 사용하는 것과 관련이 있으므로 목록인지, 유형이 무엇인지 알 때만 수행하십시오.


일반적으로 불가능하기 때문에 List<String>List<Integer>같은 런타임 클래스를 공유 할 수 있습니다.

그러나 목록을 보유한 필드의 선언 된 유형을 반영 할 수 있습니다 (선언 된 유형 자체가 값을 모르는 유형 매개 변수를 참조하지 않는 경우).


다른 사람들이 말했듯이, 유일한 정답은 아니오입니다. 유형이 지워졌습니다.

리스트에 0이 아닌 요소가있는 경우 첫 번째 요소의 유형을 조사 할 수 있습니다 (예 : getClass 메소드 사용). 그것은 당신에게리스트의 제네릭 타입을 말하지는 않지만 제네릭 타입이리스트에있는 타입의 수퍼 클래스라고 가정하는 것이 합리적입니다.

나는 접근 방식을 옹호하지는 않지만 바인드에서는 유용 할 수 있습니다.


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

유형이 지워져 사용할 수 없습니다. http://en.wikipedia.org/wiki/Type_erasurehttp://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure를 참조하십시오 .


Reflection을 사용 Field하여 이것들 을 얻으면 다음과 같이 할 수 있습니다 : field.genericTypegeneric에 대한 정보가 들어있는 유형을 얻으려면.

참고 URL : https://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list

반응형