黑马程序员技术交流社区

标题: 通过反射得泛型类型能参数的疑惑?? [打印本页]

作者: 谢洋    时间: 2013-3-10 21:44
标题: 通过反射得泛型类型能参数的疑惑??
public static void main(String[] args){
      //因为编译后就就把泛型类限定去掉了,通过反射获取泛型的参数类型
      //Vector<Date> v1 = new Vector<Date>();
      Method applyMethod = GenericTest.class.getMethod("applyVector",Vector.class);2、//通过从字节码中得到Method对应的方法,这里已经没有<Date>这个信息了
     //可能有多个参数,
     Type[] types=applyMethod.getGenericParameterTypes();
     ParameterizedType pType = (ParameterizedType)types[0];
     //取得原始参数
     System.out.println(pType.getRawType());//class java.util.Vector
    //取得实际类型参数
    System.out.println(pType.getActualTypeArguments()[0]);//class java.sql.Date//3、既然编译后<Data>被去掉了,这里怎么又能拿到?
}
public static void applyVector(Vector<Date> v1){//编译后<Date>信息被去掉
  
}
//编译后相当于
public static void applyVector(Vector v1){//1、编译后已把<Date>信息被去掉
  
}
老师张老师在讲解加强中泛型讲的中示例:
为什么编译后泛型类型参数被去后,通过反射却还可以得到?
反射不是从字节码中得到类相应的成分?而字节码是编译后的东西,是没有泛型信息的,这怎么搞的?
作者: 沈文杰    时间: 2013-3-10 22:06
本帖最后由 沈文杰 于 2013-3-10 22:17 编辑

                //通过类的字节码获取所需方法(applyVector)
                Method applyMethod = GenericTest.class.getMethod("applyVector",Vector.class);
                //通过对象反射出所有的带的泛型参数,即Vector<Date>
                Type[] types = applyMethod.getGenericParameterTypes();
                //取出第一个元素,还是Vector<Date>
                ParameterizedType Ptypes = (ParameterizedType)types[0];
                //getActualTypeArguments() 获取到的是<Date>  中所有类型参数,如果是<K,V>则是K,V;取数组中的下标为零的即为Date。
                System.out.println(Ptypes.getActualTypeArguments()[0]);

        }
        public static void applyVector(Vector<Date> v1){
               
        }

作者: admin    时间: 2013-3-11 00:19
你可以做一个实验,写一个类A和B,如下:

package com.itheima.bean;
public class A<T> {

}

package com.itheima.bean;
public class B {

}

注意,A上有泛形,B上没有,编译后,看它们生成的字节码文件大小:





明显2个字节码文件大小不同(如果A上没有泛形,它们的文件大小是一样的)。这样可以得出一个结论,泛形信息编译后其实是会保存到字节码文件中的。

老张也没说错,他应该是说类上没有泛形了,也就是说字节码文件中不仅有类,也有类的其它信息。java的类装载器在装载字节码文件时单独装载了类(这是出于性能方面的考虑),而把类的泛形信息保存到了其它的地方,所以你在反射时还是反射得到的。但类上的确没有了泛形信息,所以我们会经常看到“擦除”这个词。

如没明白,欢迎继续提问,祝学习愉快。




作者: admin    时间: 2013-3-11 00:35
再说一句,这问题如果你上班了你可以作面试官问别人,也可以面试时设计一个场景主动跟面试官说出来,外面80%以上的程序员并不明白这问题,这问题困惑了很多人,但很多人脑袋里有过这疑惑,只是一闪就过了,没有深入研究。面试时你能让面试官学到知识,面试效果扛扛的。
作者: admin    时间: 2013-3-11 01:10
mingning179 发表于 2013-3-11 00:56
您的泛型参数是T,并不是一个具体的类型,这样依然是无法得到泛型的具体参数类型吧。
其次反射从字节码中 ...

你理解有问题,特别是“那么这个类相关字节码中应该就会保存这个泛型参数的具体类型”这句话,类的字节码中是没有泛形的具体类型的,程序在运行时,如果确定了T的类型,jvm会把类型信息保存在内存中(不是字节码文件中),所以你能反射到。

类的运行时和编译时这个概念要好好理解。

作者: admin    时间: 2013-3-11 01:27
mingning179 发表于 2013-3-11 01:17
我的意思是那个class文件哈,可能对这个字节码描述不准确。
我的意思是class文件中保存了那些东西。如果c ...

程序要运行,要先把class文件会装到内存中啊,不要老盯着那个文件,文件最终会变成内存中的class,具体类型在程序运行时才确定,也就是在内存中讨论具体类型才有意义,想想

ArrayList<String> arr = new ArrayList<String>()

这句话jvm会怎么执行。

不知道我的解答和你的问题有没有存在不对称现象,最好用代码把你的疑问表述出来,这样避免答非所问。
作者: 董霁辉    时间: 2013-3-11 01:36
学习了,管理员的讲解很形象,引申:JAVA中遇到的知识点必须要牢牢掌握 黑马必备
作者: 沈文杰    时间: 2013-3-11 08:50
学习了,希望以后还有类似的问题时候也能得到解答啊。
作者: 谢洋    时间: 2013-3-11 09:58
admin 发表于 2013-3-11 00:19
你可以做一个实验,写一个类A和B,如下:

package com.itheima.bean;

看到你这么解说,我似乎有上点明白,下面是我的对泛型运行的理解,不知这样解释行不行得通
1、以前笔记记下的个人的理解:
既然泛型是给编译器看的,编译后的代码也是确定,也没有泛型信息,那不同类型的实参调用同一份代码是怎么做到?我想是编译在编译时根据实际被不同类型实参调用,就会生成相对应不同的多份代码,这样子程序员只写一份,就可以达到被不同类型的参数调用。这样做好像行得通,但不确实不合理,生产的代码太垃圾,sun公司不可能这样子做吧
2、现在的理解:
根据你所说的以及上面代码执行的效果来看:
a、先首编译给根据被定义了泛型的成分上的泛型信息保存到字节码中的一部分(这里可以说是被编译器看的),然后把去掉泛型信息的源代码编译成字节码中的另一部分(类)。
b、那这两部分是怎么联系起来的?我想泛型信息那部分会以某种方式记住某些成分是始何被定义的,当我们调用实参时,JVM会根据传
过来的实参信息及调用那个成分(应该是地址,这样才能找到相应成分),再到字节码中泛型信息部分查找正在被调用的成分的泛型是如何被定义的,
再根据找泛型信息及实参匹配到正被调用的成分上,也就是说在执行的时才能确定参数类型。
c、另我认为实应用了泛型的成分,编译后是没法确定的参数类型,因为编译根本都没知道参数是什么类型的;
虽然泛信息和类的信息在字节码中是分开的,但这时生成类那部分的代码与普通的代码还是有区别的,那就是普通代码本身就有参数类型信息,而应用泛型的代码没有。
d、最后,如果我上面的理解是正确的,那么这我认为“泛型是给编译器看的,”这句话不是很准确,但为了便于学习,我觉得这么说还是行得通的。

如理解有误之处,强烈希望更正!!

作者: admin    时间: 2013-3-11 10:57
mingning179 发表于 2013-3-11 02:44
关于 ArrayList arr = new ArrayList();

第一,如果不对ArrayList这个类做任何改变,你是无法在程序中得 ...


第1、2点表述正确。声明泛形的类中是没有具体类型信息的,使用时才会赋予具体类型,也就是使用者类中(也就是class文件中)会有具体类型的声明。看来咱们的讨论的确存在信息不对称,我指的是泛形类本身,而你指的是赋值的那个类(这种使用者类我在概念上归于运行时)。怪我没细看你的问题。



作者: 曾志军    时间: 2013-3-27 17:10
师兄们,小弟学习学习
作者: 董延京    时间: 2013-3-27 19:09
霸道了,这个要学…
作者: Lop_adoule    时间: 2013-8-20 18:27
为自己加油 ,!
作者: Linuxgg    时间: 2014-3-6 17:13
感觉好深啊。。。看来还得慢慢学习。。。
作者: you4580    时间: 2014-5-7 15:42
基础很重要啊,继续学习中
作者: kangzhuang112    时间: 2014-6-13 18:27
技术分!!!!!!!!!!!
作者: xiaomin1992    时间: 2014-10-4 14:35
java没学啊。
作者: 含着泪微笑    时间: 2014-10-27 20:24
谢谢,老师的讲解
作者: 芭芭拉2    时间: 2014-12-11 01:14
好东西呀。我在想我怎么没这样的疑问,理解不够呀。废话少说,好好学吧!
作者: 陈佳    时间: 2015-4-2 22:07
学习了!
作者: 忘言    时间: 2015-4-16 20:23
路漫漫其修远兮~
作者: 小车车    时间: 2015-5-25 19:47
相当于讲解。。明白了!
作者: 自行车呢    时间: 2015-7-2 20:23
学习了,希望我以后也能来解答。{:2_36:}
作者: as604049322    时间: 2015-7-3 22:23
好强大
作者: taoshan    时间: 2016-5-5 20:18
反射的3个阶段。1.源文件阶段Class clazz = class.forName("类名")2.字节码阶段Class clazz = Person.class  3.创建对象阶段Class clazz = P.getclass();
作者: nicezt    时间: 2016-9-9 16:18
学习的好深入,解释也很详细清楚,受教了!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2