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泛型和反射的更多相关文章

  1. 应用Java泛型和反射导出CSV文件

    项目中有需求要把数据导出为CSV文件,因为不同的类有不同的属性,为了代码简单,应用Java的泛型和反射,写了一个函数,完成导出功能. public <T> void saveFile(Li ...

  2. Java泛型和反射总结

    A a = (A)Class.forName(“pacage.A”).newInstance(); 这和你 A a = new A(): 是一样的效果. String className = “Exa ...

  3. Java泛型反射机制(二)

    /** * @author Administrator * 好处:泛型:1安全 2减少代码重用率 */ package com.test; import java.lang.reflect.Metho ...

  4. Java基础系列 - 泛型和反射机制

    package com.test5; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Java泛型和反射机 ...

  5. java 泛型基础问题汇总

    泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. Java语言引 ...

  6. 编写高质量代码:改善Java程序的151个建议(第7章:泛型和反射___建议106~109)

    建议106:动态代理可以使代理模式更加灵活 Java的反射框架提供了动态代理(Dynamic Proxy)机制,允许在运行期对目标类生成代理,避免重复开发.我们知道一个静态代理是通过主题角色(Prox ...

  7. java的泛型与反射机制

    什么是泛型? 泛型,即“参数化类型”.顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参) ...

  8. Java高质量代码之 — 泛型与反射

    在Java5后推出了泛型,使我们在编译期间操作集合或类时更加的安全,更方便代码的阅读,而让身为编译性语言的Java提供动态性的反射技术,更是在框架开发中大行其道,从而让Java活起来,下面看一下在使用 ...

  9. JAVA的泛型与反射的联合应用

    通过泛型与反射的结合,可以编写框架来使开发更容易,这里演示的是BaseDao部分的简单使用. BaseDao部分代码: public abstract class BaseDao<T>{ ...

随机推荐

  1. vs2017通过snippet代码片断进行标准化注释

    我们在进行团队开发时,类的注释已经可以做到自定义了,详细看上篇文章<vs2017通过模块文件添加自定义注释>,而对于方法的注释,我们也需要完善一下,这里我们用到了“代码片断”插件,VS里有 ...

  2. Cause: java.sql.SQLException: 无法转换为内部表示(Mybatis)

    公司开发档案系统使用框架:Spring+Struts2+Mybatis+EasyUI,在开发过程中出现sql异常:“Cause: java.sql.SQLException: 无法转换为内部表示”,错 ...

  3. angularjs $state.go页面不刷新数据

    假如进入market/beian/add添加数据,保存提交后回退market/beian列表页,没有自动更新数据,必须得手动下拉刷新才会出来 $state.go("marketBeian&q ...

  4. Asp.net中的ViewState用法

    Session,ViewState用法基本理论:session值是保存在服务器内存上,那么,可以肯定,大量的使用session将导致服务器负担加重. 而viewstate由于只是将数据存入到页面隐藏控 ...

  5. Sql server 数据库的备份和还原数据库提示“ 加载的介质已格式化为支持 1 个介质簇,但根据指定的备份设备,应支持 2 个介质簇”

     数据库备份和还原总结 在 "M:\2017-Pro\company\other\databak_2014-10\anquanbaowei_db_201704300200.BAK" ...

  6. EL_JSTL

    EL(Expression Language)EL表达式 作用:在JSP中消灭java代码 语法: "${ }" 用法:参考jsp页面 //Servlet中,: User user ...

  7. 动态页面技术----EL技术、JSTL技术,javaEE的开发模式

    1 EL技术 1.1 EL 表达式 EL(Express Lanuage)表达式可以嵌入在jsp页面内部,减少jsp脚本的编写, EL出现的目的是要替代jsp页面中脚本的编写,就是简化java代码. ...

  8. 一篇文章读懂JSON

    什么是json? W3C JSON定义修改版: JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation) JSON 是轻量级的文本数据交换格式,并不是 ...

  9. Oracle数据库基础--建表语法+操作

    语法 1.建表 create table 表名( 列名 数据类型, …… ); 2.删除表:drop table 表名; 3.添加列:alter table 表名 add(列名 数据类型); 4.修改 ...

  10. git入门使用摘录

    无论使用github或者gitlab,第一步都是在本地生产ssh-key,ssh-key作为客户端的身份证存放在user用户的.ssh文件夹下.如果之前没有生产过,需要用ssh-keygen命令生成. ...