Java泛型和反射
1. 字节码对象的三种获取方式
以String为例
Class<? extends String> strCls = "".getClass(); Class<String> strCls2 = String.class; Class strCls3 = Class.forName("java.lang.String"); System.out.println(strCls.equals(strCls2)); // true System.out.println(strCls.equals(strCls3)); // true
对于第一种方式:通过一个String实例的getClass方法来获取,这个函数的签名如下:
public final native Class<?> getClass();
但文档中对这个函数的解释如下:
The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:
Number n = 0;
Class<? extends Number> c = n.getClass();
所以上面就要用Class<? extends String>来接收返回值了。
对于第三种方式 forName的返回值为 Class<?>,这个等价于Class。例如 List 同 List<?> 是一样的。
三种方式获取到的返回值都是一样的,因为String的字节码对象也就只有一个。
2. 泛型与反射
https://www.cnblogs.com/one777/p/7833789.html
2.1获取父类/接口中的泛型信息
static class DemoClassParent <T> { private Map<X, T> xs; private void show(Map<X, T> data, int y){} } static class DemoClassB extends DemoClassParent<Integer> {} Type type = DemoClassB.class.getGenericSuperclass(); // clazz.getGenericInterfaces() if(type instanceof ParameterizedType){ ParameterizedType pType = (ParameterizedType) type; // 如果父类泛型有多个,则这里会循环多次 for (Type type2 : pType.getActualTypeArguments()) { if(type2 instanceof Class){ Class target = (Class) type2; System.out.println(target.getName()); } } } // 输出 java.lang.Integer
2.2 获取普通类的成员泛型和函数参数泛型
static class X {} static class DemoClassA { private Map<String, X> xs; private int kkk; private void show(Map<String, X> data, int y){} } static void ayalyse(Type gType){ if(gType instanceof Class){ System.out.println("非泛型类型:" + gType.getTypeName()); return; } if(!(gType instanceof ParameterizedType)){ return; } ParameterizedType pType = (ParameterizedType) gType; System.out.println("泛型参数类型:" + pType.getRawType().getTypeName()); Type[] actualTypeArguments = pType.getActualTypeArguments(); for (Type type2 : actualTypeArguments) { if(type2 instanceof Class){ Class target = (Class) type2; System.out.println(target.getName()); } } } static void test(Class clazz){ System.out.println("---- 反射成员属性"); for(Field field:clazz.getDeclaredFields()){ Type t = field.getGenericType(); ayalyse(t); } System.out.println("---- 反射成员函数"); for(Method method : clazz.getDeclaredMethods()){ // 获取函数参数类型 for (Type type : method.getGenericParameterTypes()) { ayalyse(type); } } } public static void main(String[] args) { test(DemoClassA.class); }
运行结果:
---- 反射成员属性
泛型参数类型:java.util.Map
java.lang.String
com.example.demo.DemoApplication$X
非泛型类型:int
---- 反射成员函数
泛型参数类型:java.util.Map
java.lang.String
com.example.demo.DemoApplication$X
非泛型类型:int
2.3Type接口
可见,以上例子的关键在于解析反射出来的type接口子类。Type接口的子类有5个:ParameterizedType,TypeVariable,GenericArrayType,Class,WildcardType。(后续说的T,都是泛型类上的泛型参数T)
ParameterizedType
代表了包含泛型信息的类型。如List<String>,List<T>,DemoA<String>等
public interface ParameterizedType extends Type { //获取<>中的实际类型 Type[] getActualTypeArguments(); //获取<>前的实际类型 Type getRawType(); //如果当前类型对应的类是内部类,则返回这个内部类对应的外部类的类型,否则返回null Type getOwnerType(); }
TypeVariable
代表了不包含泛型信息的类型(不包括基本类型,如int),即定义中没有<>的类,有T,Object等。
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement { // 获取泛型类型的上限,如果没有上限(即 class Person<T>{},这里的类型变量T 没有上限),那么上限为Object Type[] getBounds(); // 获取声明该类型变量的类比如( TypeTest<T> 中的TypeTest ) D getGenericDeclaration(); // 获取类型变量在源码中定义的名称,如T String getName(); AnnotatedType[] getAnnotatedBounds(); }
演示一下前三个方法:
public class TypeTest<T extends String & Comparable<String>> {//继承String,实现接口Comparable<String>,可以用&连接多个接口 public static void main(String[] args) throws NoSuchFieldException, SecurityException, NoSuchMethodException { TypeVariable tv[] = TypeTest.class.getTypeParameters(); Type[] ts = tv[0].getBounds(); for( Type t : ts ){ System.out.println( t ); } } } 输出: class java.lang.String java.lang.Comparable<java.lang.String> public class TypeTest<T> { public static void main(String[] args) throws NoSuchFieldException, SecurityException, NoSuchMethodException { TypeVariable tv[] = TypeTest.class.getTypeParameters(); System.out.println( tv[0].getGenericDeclaration() ); System.out.println( tv[0].getName() ); } } 输出: class testProject.TypeTest T
GenericArrayType
代表了泛型数组类型。如List<T>[],List<String>[],T[],DemoA<T>[],DemoA<String>[]等。
public interface GenericArrayType extends Type { Type getGenericComponentType(); }
这个函数返回泛型数组中元素的Type类型,即List<String>[] 中的 List<String>(ParameterizedTypeImpl),T[] 中的T(TypeVariableImpl),List<String>[][]中的List<String>[]();
Class
代表了泛型参数。如List,DemoA,String,List[],int[],int.
public final class Class<T> extends Object implements Serializable,GenericDeclaration,Type,AnnotatedElement
getTypeParameters方法:返回当前类的泛型信息
class People<T,V,S>{ } class Chinese extends People<String,Integer,Double>{ } TypeVariable[] tv = People.class.getTypeParameters(); System.out.println( tv.length ); for( TypeVariable t : tv ){ System.out.println( t ); } TypeVariable[] tv1 = Chinese.class.getTypeParameters(); System.out.println( tv1.length ); 输出: 3 T V S 0
getGenericSuperClass:获取父类的泛型信息。
如( class Chinese extendis People<String,Integer,Double>{},返回的是People<String,Integer,Double>,如果没有父类,返回的是Objec的Class实例 )
getGenericInterfaces:获取接口中的泛型信息。
如(class Chinese extends People<String,Integer,Double> implements SpeakChinese<String>,UseChopsticks<Double>{},返回的是SpeakChinese<String>,UseChopsticks<Double>,如果没有实现的接口,返回的Type数组长度为0)。
WildcardType
public interface WildcardType extends Type{ Type[] getLowerBounds(); // 获取下限 Type[] getUpperBounds(); // 获取上限 }
测试代码
List<? extends String> upperBoundsList; List<? super Integer> lowerBoundsList; Field upperBoundsList = TypeTest.class.getDeclaredField("upperBoundsList"); ParameterizedType pt = (ParameterizedType)upperBoundsList.getGenericType(); Type[] types = pt.getActualTypeArguments(); System.out.println( ((WildcardType)types[0]).getUpperBounds()[0] ); // class java.lang.String Field lowerBoundsList = TypeTest.class.getDeclaredField("lowerBoundsList"); ParameterizedType pt1 = (ParameterizedType)lowerBoundsList.getGenericType(); Type[] types1 = pt1.getActualTypeArguments(); System.out.println( ((WildcardType)types1[0]).getLowerBounds()[0] ); //class java.lang.Integer
3.关于泛型擦除
由于编译期间存在泛型擦除,所以字节码对象不会因为泛型而出现差异。泛型擦除存在限制,并不是所有的泛型都会擦除,而是只有函数内创建的局部变量的泛型会被擦除。
3.1 例子1
List<String> ls = new ArrayList<String>(); System.out.println(ls.getClass().equals(ArrayList.class)); // true
3.2 例子2
List<String> list = new ArrayList<>(); list.add("string"); // list.add(true); // 编译报错 System.out.println(list); // 使用反射绕过泛型的限制,往限制为String元素的list中添加其他类型的元素 Method mt = list.getClass().getDeclaredMethod("add", Object.class); mt.invoke(list, true); System.out.println(list);
3.3父类的泛型信息不会被擦除
ps:当前类不是泛型类,但是父类是泛型类,这时候父类的泛型必须要被明确的类型指定。除非当前类为泛型类,父类的泛型沿用当前类的泛型。
以hashMap为例,当前为泛型类,而且父类的泛型沿用了当前类的泛型
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
获取泛型信息:
Map<String, Integer> map = new HashMap<String, Integer>(); Type type = map.getClass().getGenericSuperclass(); ParameterizedType parameterizedType = ParameterizedType.class.cast(type); for (Type typeArgument : parameterizedType.getActualTypeArguments()) { System.out.println(typeArgument.getTypeName()); }
输出
K
V
但是如果把
Map<String, Integer> map = new HashMap<String, Integer>();
换成
Map<String, Integer> map = new HashMap<String, Integer>(){};
以上代码就会输出:
java.lang.String
java.lang.Integer
这是因为第一种写法中,父类沿用子类的泛型,而子类的泛型被擦除了,所以获取父类的泛型就获取不到了。第二种中,相当于创建了一个HashMap的匿名内部类对象,父类的泛型信息被保存了下来。
3.4 成员变量的泛型不会被擦除
static Map<String, Integer> map = new HashMap<String, Integer>(); public static void main(String[] args) throws Exception { Type type = DemoApplication.class.getDeclaredField("map").getGenericType(); ParameterizedType parameterizedType = ParameterizedType.class.cast(type); for (Type typeArgument : parameterizedType.getActualTypeArguments()) { System.out.println(typeArgument.getTypeName()); } }
运行结果和上一个例子一样。
3.5 成员函数中的参数泛型不会被擦除
例子2.2中已经显示出来。
Java泛型和反射的更多相关文章
- 应用Java泛型和反射导出CSV文件
项目中有需求要把数据导出为CSV文件,因为不同的类有不同的属性,为了代码简单,应用Java的泛型和反射,写了一个函数,完成导出功能. public <T> void saveFile(Li ...
- Java泛型和反射总结
A a = (A)Class.forName(“pacage.A”).newInstance(); 这和你 A a = new A(): 是一样的效果. String className = “Exa ...
- Java泛型反射机制(二)
/** * @author Administrator * 好处:泛型:1安全 2减少代码重用率 */ package com.test; import java.lang.reflect.Metho ...
- Java基础系列 - 泛型和反射机制
package com.test5; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Java泛型和反射机 ...
- java 泛型基础问题汇总
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. Java语言引 ...
- 编写高质量代码:改善Java程序的151个建议(第7章:泛型和反射___建议106~109)
建议106:动态代理可以使代理模式更加灵活 Java的反射框架提供了动态代理(Dynamic Proxy)机制,允许在运行期对目标类生成代理,避免重复开发.我们知道一个静态代理是通过主题角色(Prox ...
- java的泛型与反射机制
什么是泛型? 泛型,即“参数化类型”.顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参) ...
- Java高质量代码之 — 泛型与反射
在Java5后推出了泛型,使我们在编译期间操作集合或类时更加的安全,更方便代码的阅读,而让身为编译性语言的Java提供动态性的反射技术,更是在框架开发中大行其道,从而让Java活起来,下面看一下在使用 ...
- JAVA的泛型与反射的联合应用
通过泛型与反射的结合,可以编写框架来使开发更容易,这里演示的是BaseDao部分的简单使用. BaseDao部分代码: public abstract class BaseDao<T>{ ...
随机推荐
- 【ACM】最长公共子序列 - 动态规划
最长公共子序列 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 咱们就不拐弯抹角了,如题,需要你做的就是写一个程序,得出最长公共子序列.tip:最长公共子序列也称作最 ...
- java Bean的映射工具
数据层,业务逻辑层和表现层,每一层对应的应该是不一样的对象,所以就有个这么个java bean的映射工具 dozer.......................................... ...
- Abp 添加阿里云短信发送
ABP中有短信发送接口ISmsSender public interface ISmsSender { Task<string> SendAsync(string number, stri ...
- electron 开发记录
判断是否开发环境 安装 electron-is-dev npm install electron-is-dev // main.js const isDev = require('electron-i ...
- Cucumber 场景大纲 Scenario Outlines
引用链接:https://github.com/cucumber/cucumber/wiki/Scenario-Outlines script/cucumber --i18n zh-CN | feat ...
- ubuntu命令收集
软件操作: - sudo apt-get install xxx 安装软件 - sudo apt-get --purge remove XXX 卸载软件 - sudo apt-get -f insta ...
- Json Web Token 简介
1.Json Web Token是干什么 简称JWT,在HTTP通信过程中,进行身份认证. 我们知道HTTP通信是无状态的,因此客户端的请求到了服务端处理完之后是无法返回给原 ...
- C#对话框-打开和保存对话框(转)
//打开文件 OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.In ...
- jeesite应用实战(数据增删改查),认真读完后10分钟就能开发一个模块
jeesite配置指南(官方文档有坑,我把坑填了!)这篇文章里,我主要把jeesite官方给出的帮助文档的坑填了,按照里面的方法可以搭建起来jeesite的站点.系统可以运行以后,就可以进入开发模块了 ...
- LeetCode Valid Anagram (简单题)
题意: 给出两个字符串s和t,判断串t是否为s打乱后的串. 思路: 如果返回的是true,则两个串的长度必定相等,所有字符出现的次数一样.那么可以统计26个字母的次数来解决,复杂度O(n).也可以排序 ...