美文网首页
泛型的反射问题分析

泛型的反射问题分析

作者: 耗子2015 | 来源:发表于2018-12-18 16:35 被阅读922次

背景和问题

我们需要通过方法的参数类型,创建参数的实例。本地开发测试正常,部署测试环境提示反射异常。

为便于理解,改为学生与学校的关系表示。

代码:

interface IStudent {
    void learn();
}

interface ISchool<R extends IStudent> {
    void add(R r);
}

class MiddleStudent implements IStudent{
    @Override
    public void learn() {
    //TODO
    }
}

class MiddleSchool implements ISchool<MiddleStudent> {
    @Override
    public void add(MiddleStudent middleStudent) {
    /TODO
    }
}
public class Test {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        //所有方法
        Method[] methods = MiddleSchool.class.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
            //找add方法
            if("add".equals(method.getName())){
                //取第一个参数的class
                Class clazz = method.getParameterTypes()[0];
                //创建实例
                clazz.newInstance();
            }
        }
    }
}

异常

Exception in thread "main" java.lang.InstantiationException: IStudent
    at java.lang.Class.newInstance(Class.java:427)
    at Test.main(Test.java:44)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.NoSuchMethodException: IStudent.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.newInstance(Class.java:412)
    ... 6 more  

通过debug,发现add同名方法有两个,参数分别是MiddleStudent和IStudent,而IStudent是接口,不能实例化。解决方式:

//判断class类型是接口,返回
if(clazz.isInterface()){
    continue;
}

为什么会有两个add方法呢?

javap -verbose MiddleSchool.class

public void add(MiddleStudent);
    descriptor: (LMiddleStudent;)V
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=2, args_size=2
         0: return
      LineNumberTable:
        line 29: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  this   LMiddleSchool;
            0       1     1 middleStudent   LMiddleStudent;

  public void add(IStudent);
    descriptor: (LIStudent;)V
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: checkcast     #2                  // class MiddleStudent
         5: invokevirtual #3                  // Method add:(LMiddleStudent;)V
         8: return
      LineNumberTable:
        line 24: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   LMiddleSchool;

编译后已经是两个add方法了,其中add(IStudent)的flags值ACC_BRIDGE,表示桥接方法(桥接方法实际是调用了实际的泛型方法)

为什么要生成桥接方法呢?

对于明确或不明确的泛型类,保证兼容性。如List类:

 public static void main(String[] args) {
        //明确泛型
        List<String> listString = new ArrayList<String>();
        listString.add("abc");
        //不明确泛型
        List list = new ArrayList<>();
        list.add(new Object());
        //结果为true,可见class是同一个
        System.out.println(listString.getClass() == list.getClass());
    }

泛型在编译时编译器会检查往集合中添加的对象的类型是否匹配泛型类型,如果不正确会在编译时就会发现错误,而不必等到运行时才发现错误。因为泛型是在1.5引入的,为了向前兼容,所以会在编译时去掉泛型(泛型擦除),但是我们还是可以通过反射API来获取泛型的信息,在编译时可以通过泛型来保证类型的正确性,而不必等到运行时才发现类型不正确。由于java泛型的擦除特性,如果不生成桥接方法,那么与1.5之前的字节码就不兼容了。

知识点:

  1. 泛型类型擦除 type erasure
  2. 桥接方法

感谢前人栽树

  1. https://www.ibm.com/developerworks/cn/java/j-jtp01255.html
  2. https://rednaxelafx.iteye.com/blog/586212
  3. https://blog.csdn.net/mhmyqn/article/details/47342577

相关文章

  • 泛型的反射问题分析

    背景和问题 我们需要通过方法的参数类型,创建参数的实例。本地开发测试正常,部署测试环境提示反射异常。 为便于理解,...

  • 泛型 & 注解 & Log4J日志组件

    掌握的知识 : 基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例) 泛型 概述 : 泛型...

  • 章节笔记——泛型与反射机制

    泛型 反射机制 泛型:由于类型转换错误,编译时不报错,运行时报错。为了弥补这个问题,用泛型弥补,集合类添加对象不需...

  • Java泛型

    本文介绍的知识点 泛型是什么? 泛型的使用在反射中使用泛型在集合类中使用泛型 关于泛型擦除如何理解?如何避免泛型擦...

  • 【Java】【反射】泛型反射

    泛型反射 在运行时,泛型是无效的,所以可以通过反射在运行时将其他类型变量添加到集合,而不需要考虑泛型

  • 关于反射的使用

    反射中获取泛型参数信息

  • java泛型-1-概述

    问题 反射怎么获取泛型https://blog.csdn.net/qq_30698633/article/deta...

  • Java核心技术(卷I) 11、反射

    能够分析类能力的程序称为反射 1、反射机制的作用 在运行时分析类的能力 在运行时检查对象 实现泛型数组操作代码 利...

  • 定个Java学习目标,希望能进菜鸟网络

    JAVA语言基础: java基本类型、引用类型、多态底层、泛型底层、反射机制 常见的集合类源码分析hashMap、...

  • Java笔记---泛型

    总结了泛型的基本语法、上下级通配符、泛型反射以及使用泛型的一些实践,看完还不会用泛型你顺着网线来打我(狗头)。 我...

网友评论

      本文标题:泛型的反射问题分析

      本文链接:https://www.haomeiwen.com/subject/temkkqtx.html