之前刷B站遇到一个很有意思的问题,就是一个list里面被塞入了String类型的数值,我的第一个想法是触发了一个类型强制转换所以才能赋值成功,没想到不是这样这和java泛型机制有关,java的泛型其实是假泛型类型T在运行的时候已经被擦除,如果通过反射注入值不判断类型是存在一些类型不安全的隐患的。
问题的一个简单说明
这个问题是不是很神奇,list<integer>里面居然输出了字符串。在值赋值的时候无感,但是在取值使用的时候就会报错了。但是这个取第0个值的时候也没有报错,这也是我现在也比较迷惑的地方。
下面这个链接就是视频出处。
【一个非常隐蔽的BUG!泛型的正确使用方式!高级面试高频题!-哔哩哔哩】 https://b23.tv/GppCwWz
问题复现
这个copy的赋值逻辑大概猜想了一下。
public class 集合泛型强转 {
List<Integer> i1;
List<String> s1;
@Before
public void b1(){
i1=new ArrayList<>();;
s1=new ArrayList<>();
s1.addAll(Arrays.asList("s1","s2","s3"));
}
@Test
public void t1() throws NoSuchFieldException, IllegalAccessException {
//获得一个list的class对象
Class<? extends List> listClass = s1.getClass();
//获得需要的两个反射字段,目前就我的了解来说需要这两个字段 elementData存放元素、size存放含有多少元素
//目前来说arraylist应该可以用 其他的list子类具体分析
Field elementData = listClass.getDeclaredField("elementData");
Field size = listClass.getDeclaredField("size");
//设置字段可访问
size.setAccessible(true);
elementData.setAccessible(true);
//获取s1的elementData元素值
Object o = elementData.get(s1);
//设置i1的元素值和长度
elementData.set(i1,o);
size.set(i1,s1.size());
System.out.println(i1.get(0));
}
}
i1里面就存在了一些非Integer的数值类型了
如果我想获取这个参数的泛型怎么解决呢?我暂时想不到,下面的就是通过类的对象属性来获取泛型类型
@Test
public void t2(){
Class<集合泛型强转> c1 = 集合泛型强转.class;
Field[] declaredFields = c1.getDeclaredFields();
for (Field f:declaredFields){
Type genericType = f.getGenericType();
//如果类型带有泛型参数
if (genericType instanceof ParameterizedType) {
//ParameterizedType进行类型参数转换
genericType=(ParameterizedType)genericType;
Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
//因为泛型参数只有一个就直接取0了
Type actualTypeArgument = actualTypeArguments[0];
System.out.println(actualTypeArgument);
}
}
}
结果是正确的
一个问题
如果这个参数不在类里面,而只是一个普普通的形式参数,我该怎么判断他们的泛型是否一致呢?
我想到的:1、还是通过泛型类型来限制他们必须是同一类型的值
2、就是一个很简单的判断,当然要判断非空,既然是赋值操作如果源对象为空接下来的步骤都不用走了
@Test
public void t3(){
System.out.println(s1.get(0).getClass());
}
有其他想法的可以下面留言,一起交流进步。