首先,java中有泛型擦除这一概念。如果不太了解这一概念的同学可以自己找一下度娘。(简单来说:就是如果使用了泛型,在编译期的时候泛型会被擦除掉,也就是说jvm所看到的class文件中是不存在泛型这一概念和东西的。)
好。进入主题,关于泛型擦除,但是反编译class文件泛型会出现的问题。以例子来说明
首先,先写一个使用了泛型的java文件
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* Created by beyondLi on 2017/5/26.
*/
public class GenericsTest {
@Test
public void generics(){
//首先创建一个list集合,并使用泛型
List<Integer> list = new ArrayList<>();
list.add(100);
list.add(200);
System.out.println(list);
}
}
使用反编译工具查看生成的class文件(idea自带反编译插件)
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class GenericsTest {
public GenericsTest() {
}
@Test
public void generics() {
List<Integer> list = new ArrayList();
list.add(Integer.valueOf(100));
list.add(Integer.valueOf(200));
System.out.println(list);
}
}
奇怪,泛型不是被擦除了吗。为什么反编译还会存在。肯定有同学会说。和反编译工具有关。这里我使用了jd-gui和Beyond Compare分别进行了反编译结果如下
gui
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class GenericsTest
{
@Test
public void generics()
{
List<Integer> list = new ArrayList();
list.add(Integer.valueOf(100));
list.add(Integer.valueOf(200));
System.out.println(list);
}
}
Compare
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
public class GenericsTest
{
public GenericsTest()
{
}
public void generics()
{
List list = new ArrayList();
list.add(Integer.valueOf(100));
list.add(Integer.valueOf(200));
System.out.println(list);
}
}
肯定有同学说,你看。就说和反编译工具有关吧。Compare不就没有泛型了吗。官方都说了会泛型擦除怎么会有问题。
但是还是存在一个问题,那gui又是怎么找到的泛型呢?idea呢?
最后我使用了java自带的反编译工具javap -c 来进行反编译,来看看到底是怎么回事。
Compiled from "GenericsTest.java"
public class GenericsTest {
public GenericsTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void generics();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: bipush 100
11: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
19: pop
20: aload_1
21: sipush 200
24: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
27: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
32: pop
33: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
36: aload_1
37: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
40: return
}
虽然本人也不是很能看懂java自带的反编译工具编译出来的结果,但是可以得到的结论是,从不同的反编译工具中可以看出,至少反编译工具是可以从class文件中找到你之前使用的泛型的,证明文件中是存在标记来记录这个泛型的,然后从javap中我们可以看出,虽然泛型不在代码中了,但是他还是记录在了注释中,这样就可以解释通为什么有的反编译工具可以反编译出泛型了。
泛型在编译期会被擦除的结论是没有问题的,在jvm中不存在泛型的概念。但是反编译工具通过注释中的记录找到了之前使用过的泛型类型,并在反编译时将其添加回来,所以我们所看到的反编译的文件中泛型存在,好像与泛型擦除这一概念冲突了。然而事实证明并没有,只是反编译工具太智能了而已。
以上为本人个人的见解,并且对javap所反编译出来的文件也无法达到完全看懂的地步,如有哪里错误,还希望指出,可让我继续成长。