我有;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
有没有一种(简单的)方法来检索列表的通用类型?
我有;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
有没有一种(简单的)方法来检索列表的通用类型?
Answers:
如果这些实际上是某个类的字段,那么您可以在反射的帮助下获得它们:
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>
和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
并将它们从列表中拉出时,可以根据它们的类型将它们提供给适当的处理程序。
查找一个字段的通用类型:
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
如果需要获取返回类型的泛型类型,当我需要在返回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
扩展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
)。
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>
和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>
和List<String>
;在这种情况下,类型擦除不适用。
使用Reflection来获取Field
这些信息,然后您可以做得到:field.genericType
也获取包含有关通用信息的类型。