我们知道,一个Java的类主要包括两个元素,即是成员变量和成员方法。成员变量包括实例成员变量和静态成员变量,而成员方法也有实例成员方法和静态成员方法,构造方法则是特殊的成员方法。而反射的主要作用是能够在运行时获取一个Class的各个元素的结构,但无法更改这些元素的结构。这些元素就是前面说的成员变量和成员方法,并且对于成员变量,反射可以对其进行设值和取值,对于成员方法,反射可以直接对其进行方法调用,而对于特殊的成员方法构造方法而言,反射可以调用构造方法元素的newInstance()方法直接实例化一个该类的对象,而不需要使用new的方式。
那么现在问题来了,在使用反射获取一个Class的各个元素的结构时,哪些地方可以直接获取得到泛型类型参数,并且能够将该泛型类型参数直接实例化成一个对象呢?
在对反射进行深入研究后发现,有以下三种情况可以实现:
一. 成员变量类型的泛型参数。
二. 成员方法返回值的泛型参数。
三. 成员方法参数类型的泛型参数,包括构造方法。
在说到泛型类型参数时,要先说一下Type接口,Type接口只有一个实现类Class,但是有四个子接口,这四个Type子接口描述了Java泛型的四种形式。分别是:
GenericArrayType 该接口表示一种数组类型,其组件类型为参数化类型或类型变量,如参数化类型数组Map<String, Date>[] mapArr,还有类型变量数组T[] tArr
ParameterizedType 该接口表示参数化类型,如 Collection<String>。
TypeVariable<D> 该接口是各种类型变量的公共高级接口,可以表示泛型声明的参数类型(不存在的类型),如 class ClassName<T>和Collection<T>,这里的T就是一个类型变量。
WildcardType 表示一个通配符类型表达式,如 ?、? extends Number 或 ? super Integer。
在Java泛型的这四种形式中,类型变量和类型变量数组还有通配符类型表达式都是取不到泛型参数的实际类型的,只有泛型的参数化类型和参数化类型数组中的实际类型可以得到实际类型,并可用于实例化,也就是说只有前两种情况GenericArrayType和ParameterizedType才有可能得到泛型的实际类型。
下面结合例子详细说明可以取到泛型实际类型的三种情况:
在这三种情况中,第一步都是得到一个Type类型或Type数组。
一. 成员变量类型的泛型参数。
Field类下有个Type getGenericType()方法可以获取泛型类,返回类型为Type,代码如下:
Type type = field. getGenericType();
二. 成员方法返回值的泛型参数。
Method类下有个Type getGenericReturnType()方法可以获取成员方法返回值的泛型类,返回类型为Type,代码如下:
Type type = method. getGenericReturnType();
三. 成员方法参数类型的泛型参数,包括构造方法。
Method类下有个Type[] getGenericParameterTypes()方法可以获得成员方法各个参数的泛型类的数组,返回值为Type[],(Constructor类里面也有一个getGenericParameterTypes()方法,也可以使用相同的方式取到构造器参数里的实际泛型参数)代码如下:
Type[] typeArr = method. getGenericParameterTypes();
另一种方式是使用Method类下的Parameter[] getParameters()方法先获取各个参数组成的数组Parameter [],然后再使用Parameter类下的Type getParameterizedType()方法获取参数的泛型类,返回值为Type。(这种方式是Java8以后才有的,因为Parameter是Java8才新增的表示方法参数元素的类型。getParameters()方法其实是Method继承自Executable抽象类里面的方法,Executable抽象类是也是Java8新增的,是Method和Constructor两个类的基类,所以构造方法也可以使用这个方式得到构造器参数里的实际泛型参数)
代码如下:
Parameter [] parameterArr = Method. getParameters();
Type[] typeArr = new Type[parameterArr.length];
for(i=0;i<parameterArr.length;i++){
typeArr[i] = parameterArr[i]. getParameterizedType();
}
在第一步得到的Type类型的对象后,这个对象有可能是Type接口的四个子接口实现类的实例。在使用测试代码测试后发现,这个对象只会是GenericArrayType和ParameterizedType两个接口的实现类的实例。
所以第二步我们要做的就是将其封装成一个递归方法,如以下代码所示:
void instanceActualTypeArguments(Type type) throws Exception{
System.out.println("该类型是"+ type);
// 参数化类型
if ( type instanceof ParameterizedType ) {
Type[] typeArguments = ((ParameterizedType)type).getActualTypeArguments();
for (int i = 0; i < typeArguments.length; i++) {
// 类型变量
if(typeArguments[i] instanceof TypeVariable){
System.out.println("第" + (i+1) + "个泛型参数类型是类型变量" + typeArguments[i] + ",无法实例化。");
}
// 通配符表达式
else if(typeArguments[i] instanceof WildcardType){
System.out.println("第" + (i+1) + "个泛型参数类型是通配符表达式" + typeArguments[i] + ",无法实例化。");
}
// 泛型的实际类型,即实际存在的类型
else if(typeArguments[i] instanceof Class){
System.out.println("第" + (i+1) + "个泛型参数类型是:" + typeArguments[i] + ",可以直接实例化对象");
}
}
// 参数化类型数组或类型变量数组
} else if ( type instanceof GenericArrayType) {
System.out.println("该泛型类型是参数化类型数组或类型变量数组,可以获取其原始类型。");
Type componentType = ((GenericArrayType)type).getGenericComponentType();
// 类型变量
if(componentType instanceof TypeVariable){
System.out.println("该类型变量数组的原始类型是类型变量" + componentType + ",无法实例化。");
}
// 参数化类型,参数化类型数组或类型变量数组
// 参数化类型数组或类型变量数组也可以是多维的数组,getGenericComponentType()方法仅仅是去掉最右边的[]
else {
// 递归调用方法自身
instanceActualTypeArguments(componentType);
}
} else if( type instanceof TypeVariable){
System.out.println("该类型是类型变量");
}else if( type instanceof WildcardType){
System.out.println("该类型是通配符表达式");
} else if( type instanceof Class ){
System.out.println("该类型不是泛型类型");
} else {
throw new Exception();
}
}
从上面的分析可知,我们使用反射时获取泛型类型参数时只需要两步就行了。在实际编写代码的时候,第二步需要根据需求进行相应的更改。
下面用一个完整的程序测试前面说到的反射时获取泛型类型参数的方法。
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.List;
import java.util.Map;
public class TypeArguments<T> {
public TypeArguments(){}
public <E> TypeArguments(E e){}
public Map<T, String> genericField;
public <B> Map<Integer, String>[] genericMethod(List<? extends Integer> list, List<String> list2, String str, B[] tArr) throws IOException, NoSuchMethodException{
return null;
}
public static void main(String[] args) throws Exception {
Class<?> clazz = TypeArguments.class;
System.out.println("一. 成员变量类型的泛型参数");
Field field = clazz.getField("genericField");
Type fieldGenericType = field.getGenericType();
instanceActualTypeArguments(fieldGenericType);
System.out.println();
System.out.println("二. 成员方法返回值的泛型参数。");
Method method = clazz.getMethod("genericMethod", new Class<?>[]{List.class, List.class, String.class, Object[].class});
Type genericReturnType = method.getGenericReturnType();
instanceActualTypeArguments(genericReturnType);
System.out.println();
System.out.println("三. 成员方法参数类型的泛型参数。");
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
System.out.println("该方法的第" + (i+1) + "个参数:");
instanceActualTypeArguments(genericParameterTypes[i]);
}
System.out.println();
System.out.println("三. 构造方法参数类型的泛型参数。");
Constructor<?> constructor = clazz.getConstructor(new Class<?>[]{Object.class});
Type[] constructorParameterTypes = constructor.getGenericParameterTypes();
for (int i = 0; i < constructorParameterTypes.length; i++) {
System.out.println("该构造方法的第" + (i+1) + "个参数:");
instanceActualTypeArguments(constructorParameterTypes[i]);
}
System.out.println();
}
/**
* 实例化泛型的实际类型参数
* @param type
* @throws Exception
*/
private static void instanceActualTypeArguments(Type type) throws Exception{
System.out.println("该类型是"+ type);
// 参数化类型
if ( type instanceof ParameterizedType ) {
Type[] typeArguments = ((ParameterizedType)type).getActualTypeArguments();
for (int i = 0; i < typeArguments.length; i++) {
// 类型变量
if(typeArguments[i] instanceof TypeVariable){
System.out.println("第" + (i+1) + "个泛型参数类型是类型变量" + typeArguments[i] + ",无法实例化。");
}
// 通配符表达式
else if(typeArguments[i] instanceof WildcardType){
System.out.println("第" + (i+1) + "个泛型参数类型是通配符表达式" + typeArguments[i] + ",无法实例化。");
}
// 泛型的实际类型,即实际存在的类型
else if(typeArguments[i] instanceof Class){
System.out.println("第" + (i+1) + "个泛型参数类型是:" + typeArguments[i] + ",可以直接实例化对象");
}
}
// 参数化类型数组或类型变量数组
} else if ( type instanceof GenericArrayType) {
System.out.println("该泛型类型是参数化类型数组或类型变量数组,可以获取其原始类型。");
Type componentType = ((GenericArrayType)type).getGenericComponentType();
// 类型变量
if(componentType instanceof TypeVariable){
System.out.println("该类型变量数组的原始类型是类型变量" + componentType + ",无法实例化。");
}
// 参数化类型,参数化类型数组或类型变量数组
// 参数化类型数组或类型变量数组也可以是多维的数组,getGenericComponentType()方法仅仅是去掉最右边的[]
else {
// 递归调用方法自身
instanceActualTypeArguments(componentType);
}
} else if( type instanceof TypeVariable){
System.out.println("该类型是类型变量");
}else if( type instanceof WildcardType){
System.out.println("该类型是通配符表达式");
} else if( type instanceof Class ){
System.out.println("该类型不是泛型类型");
} else {
throw new Exception();
}
}
}
代码的运行结果如下:
在我写完这篇博客的时候,我发现了这么一句话,
引用
Java泛型有这么一种规律:
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。
这句话原文出自RednaxelaFX的博客"答复: Java获得泛型类型",
http://rednaxelafx.iteye.com/blog/586212
而我这里探讨的泛型则符合位于声明一侧的。
- 大小: 43.3 KB
分享到:
相关推荐
2. **类型擦除**: 泛型在编译后会进行类型擦除,因此在运行时无法直接获取泛型类型信息。但泛型可以提供编译时的类型检查。 3. **通配符**: 通配符用于表示对类型的一般限制,如`?`表示任何类型,`? extends Number...
例如,`List<?>`的`Class`类型是`java.util.List`,这意味着你不能直接获取泛型的实际类型参数。然而,你可以通过类型检查和转换来操作List中的元素。 5. 访问私有成员: 对于私有字段和方法,可以使用`...
- 反射API在泛型类型擦除后仍然能获取到泛型信息,这主要通过泛型类的类型参数的实际类型参数化实例(TypeToken)来实现。 - 这使得在运行时可以进行一些泛型相关的操作,如创建参数化的类实例。 总结来说,Java...
然而,由于类型擦除,运行时无法直接获取泛型类型参数。但是,我们可以通过检查方法或字段的签名来间接获取这些信息。 例如,假设我们有一个如下父类: ```java public class ParentClass<T> { private List<T> ...
反射是Java语言的一个特性,它允许程序在运行时获得任何类的内部信息,并且能够操作类或对象的内部属性和方法。通过反射,可以动态地创建对象,调用方法,访问属性,甚至可以解析存储在文件系统中的类定义文件(....
Java反射和泛型是两种强大的编程特性,它们在开发中有着广泛的应用,特别是在数据库操作的动态化场景下。本文将详细介绍如何结合这两种技术实现在Java中对MySQL、SQL Server或Oracle数据库进行动态的增删改查操作。 ...
Java 泛型还支持类型参数的边界,比如 `T extends SomeClass`,这样可以限制类型参数必须是 `SomeClass` 或其子类。例如,`List<String> list = new ArrayList();` 中的 `List<String>` 指定了列表元素只能是字符串...
泛型是Java 5引入的新特性,它允许在类、接口和方法中使用类型参数,从而提高了代码的类型安全性和可读性。在`BaseDao.java`中,可能会定义一个泛型基类,如: ```java public abstract class BaseDao<T> { public...
Java反射机制是Java语言的一个重要特性,它允许运行时访问类、接口、字段和方法的信息,包括私有成员。在处理泛型时,由于Java的类型擦除,直接使用常规方式无法获取到泛型参数的具体类型。这时,就需要借助反射来...
最后,我们使用`getActualTypeArguments()`方法获取Type对象中的实际类型参数,然后将其强制转换为Class对象。 四、泛型的使用场景 泛型可以应用于多种场景中,例如: * 集合框架:泛型可以用于集合框架中,例如...
`如果泛型是List,则可以通过检查其是否为ParameterizedType并获取实际类型参数。 7. Java 8的新特性: - 默认方法:Java 8引入了接口的默认方法,反射也可以调用这些方法。 - 方法引用和构造器引用:这使得反射...
Java泛型是Java编程语言中一个强大的特性,它允许在定义类、接口和方法时使用类型参数,从而实现参数化类型。泛型的主要目标是提高代码的类型安全性和重用性,减少类型转换的麻烦,并在编译时捕获可能的类型错误。...
1. **类型参数**:定义泛型类或接口时,使用尖括号`<T>`表示类型参数,T是占位符,代表任意类型。 2. **通配符**:如`?`,表示未知类型,常用于方法参数,提供更大的灵活性。 3. **边界**:`<T extends SomeClass>`...
Java反射和泛型是Java编程中的两个重要特性,它们各自有着独特的作用,但在某些场景下也可以结合使用。本文将深入探讨这两个概念,并提供一个综合性的例子来帮助理解。 首先,让我们了解一下Java反射。反射是在运行...
在Java编程中,注解...这个示例涵盖了Java中使用反射获取注解的基本操作,包括类、方法、注解的值和方法参数。通过实践这个例子,你可以更好地理解和掌握这些概念,从而在实际项目中更有效地利用注解和反射。
1. 反射(Reflection):Java反射机制允许程序在运行时动态地获取类的信息(如类名、方法名、参数类型等)并调用其方法。通过`Class`类,我们可以实例化未知类型的对象,调用私有方法,访问私有字段,以及检查类的...
如果类具有泛型参数,我们可以使用反射来获取实际的泛型类型,从而更好地理解实例化的对象。 2. **动态调用方法**:使用`Class.getMethod()`或`Class.getDeclaredMethod()`获取方法对象,然后调用`invoke()`方法...
在本主题中,我们将深入探讨如何通过反射获取泛型参数,并利用这些信息创建对象。 首先,让我们了解什么是反射。反射是Java提供的一种强大的工具,它允许我们在运行时检查类、接口、字段和方法的信息。通过反射,...
在调用泛型方法时,Java编译器会根据传入的实际参数类型推断出类型参数的具体类型。例如: ```java int result = sum(10, 20); // 实际类型为 int String combined = sum("Hello ", "World"); // 实际类型为 String...
通过反射获得指定类的父类的泛型参数的实际类型