当前位置: 首页 > 科技观察

十分钟详解Java泛型理解泛型擦除

时间:2023-03-15 17:07:17 科技观察

今天我们来讲解泛型中的另一个重要知识点——泛型擦除!泛型擦除的概念泛型信息只存在于代码编译阶段,但在java运行期间(字节码文件生成后),与泛型相关的信息会被擦除。技术术语称为类型擦除。我们来看一个例子:ArrayListl1=newArrayList();ArrayListl2=newArrayList();System.out.println(l1.getClass()==l2.getClass());运行代码,结果为True是因为jvm中ArrayList和ArrayList的Class都是List.class,相当于jvm中的List。通过下面的例子我们做进一步的分析importjava.lang.reflect.Field;publicclassGeneErasure{Tobject;publicGeneErasure(Tobject){this.object=object;}publicstaticvoidmain(String[]args){GeneErasuredemo=newGeneErasure("hi");Classclassz=demo.getClass();System.out.println(classz.getName());//输出com.my.generic.GeneErasureField[]fs=classz.getDeclaredFields();for(Fieldf:fs){System.out.println("feild:"+f.getName()+"type:"+f.getType().getName());//outputfeild:objecttype:java.lang.Object}}通过这个例子我们可以看出Class的类型还是GeneErasure的GeneErasure,并且类型T被Object类型替换。接下来,我们再做一次尝试,将GeneErasure改为GeneErasure输出结果为:feild:objecttype:java.lang.String那么,当泛型类被类型擦除时,之前的泛型如果类型类的参数部分没有指定上限,比如,会被翻译成普通的Object类型。如果指定了上限,例如,则类型参数将替换为类型上限。大家都知道使用类型擦除做“坏事”不会编译下面的代码l.add(123)因为123不是String类型,这是使用泛型的好处之一。ArrayListl=newArrayList();l.add("abc");l.add(123);但是我们理解了泛型擦除的原理,我们就可以巧妙地利用这个原理结合反射知识做一些“坏事”,例如:ArrayListl=newArrayList();l.add("abc");try{Methodmethod=l.getClass().getDeclaredMethod("add",Object.class);method.invoke(l,"test");method.invoke(l,100.f);}catch(Exceptione){e.printStackTrace();}System.out.println("列表的大小为:"+l.size());for(Objecto:l){System.out.println(o);}结果操作为:list的大小为:3abctest100.0(成功插入到ArrayList中)可以看到100.0成功插入到ArrayList中,所以利用类型擦除的原理结合反射的手段绕过操作编译器在正常开发中不允许的限制。通俗的理解,我们可以把泛型比作一个看守,看守我们的代码安全,然后设置各种规定,“xxx禁止进出”提醒。现实生活中,总会有人能够绕过他们的监控(反射结合泛型擦除),按照守卫的生活规矩干一些坏事。