我今天被爱学习的同学问到了如何在List<String>里面放一个int的数据
比如
List<String> list = new ArrayList<String>();
int a = 1;
list.add(a);
这样写肯定报错了。毫无疑问。
先不管为什么他要给我来一个不合理的问题。先管实现。
这里我首先考虑到一个事情,泛型是什么,不加泛型的话
List list = new ArrayList();
int a = 1;
String str = "1"
list.add(a);
list.add(str);
是不会报错的。
那么泛型这个到底做了啥?
深究是不可能的,这辈子都不想去深究。
但是综合经验来看。
这是一个语法糖。
所谓语法糖,只是方便程序员写的,减少异常出现可能性等的一个手段,实际上编译器最后会把语法糖废掉,并用基础的方式去搞
如果把编译好的自己码.class文件拿去反编译,你会发现
List<String> list = new ArrayList<String>();
String a= "1"
list.add(a);
编译,并反编译,下面是反编译的结果
List list = new ArrayList();
String a = "1";
list.add(a);
是的,<String>被废了,当我添加一个数据获取操作的时候,计算长度
list.get(0).length();
编译,并反编译,下面是反编译的结果
((String)list.get(0)).length();
嗯,原来如此,看到了吧。编译器给我们来了一个(String)的类型转换。
那么
List list = new ArrayList();
String a= "1";
list.add(a);
((String) list.get(0)).length();
这段代码的编译结果和上面那段的一样,也就是少了<String>泛型约束,多了(String)类型转换。稍加思考,你会懂得的。
所以如何在List<String>里放int呢?
通过上面的分析,我们可以得出某种结论:用了泛型,经过编译器后,会出现”类型擦除“的现象,并添加强制类型转换的操作。
那么,如果我自己做类型擦出呢?
这里有个故事,昨天晚上我想到用反射,嗯,成功了,于是我今天来写文章,但是刚刚写着写着,我,发现,类型擦除,并不需要反射,看代码
1 List<String> list = new ArrayList<String>();
2 list.add("str");
3 List list2 = list;
4 list2.add(1);
5 System.out.println(list);
6 System.out.println((int)((Object)list.get(1))+1);
第一行我声明一个泛型List<String> list
第二行给list添加一个字符串,目的是做参照
第三行生么一个无泛型的 List list2 ,并让他=list,注意,这里是地址引用,也就是list2和list指向同一个对象地址空间!(细节百度,不长篇大论)
第四行给list2放一个int类型的数字
然后打印list,发现,塞进去了?
并且做数学运算成功了?
看下字节码反编译:
List list = new ArrayList();
list.add("str");
List list2 = list;
list2.add(Integer.valueOf(1));
System.out.println(list);
System.out.println(((Integer)list.get(1)).intValue() + 1);
他做了啥呀,list的类型擦除,lsit2添加int,list.get(1)的一系列操作。
再解释就不对了,我本来只想说怎么放的,几行代码的事情,一时兴起就搞一下编译器相关,顺便优化了昨天反射的方法,还不错,所谓的温故而知新。古人诚不欺我!
理解这个需要
java语法(写了这么多年)
懂得jvm内存原理(我一知半解)
编译器原理(我猜的)
我的程序员守则(我先管实现,再管你和不合理,傻逼要在List<String>里面放int,这违背了java语言泛型的设计初衷,不过他说这是某教学视频的课后习题)