这是我在抖音二面的时候自我感受没有答好的一题。由于个人中心只是围绕在了T
被Object
替换的问题上了,并无去讲解他会带来的问题。java
其实咱们很常见这个问题,你甚至常常用,只是没有去注意罢了,可是很不碰巧这样的问题就容易被面试官抓住。下面先来看一段代码吧。面试
List list = new ArrayList();
List listString = new ArrayList<String>();
List listInteger = new ArrayList<Integer>();
复制代码
这几段代码简单、粗暴、又带有很浓厚的熟悉感是吧。那我接下来要把一个数字1
插入到这三段不同的代码中了。dom
做为读者的你可能如今已经黑人问号了????你确定有不少疑问,这明显不同啊,怎么可能。函数
public class Main {
public static void main(String[] args) {
List list = new ArrayList();
List listString = new ArrayList<String>();
List listInteger = new ArrayList<Integer>();
try {
list.getClass().getMethod("add", Object.class).invoke(list, 1);
listString.getClass().getMethod("add", Object.class).invoke(listString, 1);
// 给不服气的读者们的测试之处,你能够改为字符串来尝试。
listInteger.getClass().getMethod("add", Object.class).invoke(listInteger, 1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("list size:" + list.size());
System.out.println("listString size:" + listString.size());
System.out.println("listInteger size:" + listInteger.size());
}
}
复制代码
上述的就是泛型擦除的一种表现了,可是为了更好的理解,固然要更深刻了是吧。虽然List
很大,但却也不是不能看看。post
两个关键点,来验证一下:学习
// 先来看看画了一个大饼的List
// 可以过很清楚的看到泛型E
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
// 第一个关键点
// 还没开始就出问题的存储类型
// 难道不该该也是一个泛型E?
transient Object[] elementData;
public E get(int index) {
rangeCheck(index);
return elementData(index); // 1---->
}
// 由1直接调用的函数
// 第二个关键点,强制转化得来的数据
E elementData(int index) {
return (E) elementData[index];
}
}
复制代码
我想,其实你也可以懂了,这个所谓的泛型T
最后会被转化为一个Object
,最后又经过强制转化来进行一个转变。从这里咱们也就可以知道为何咱们的数据从前面过来的时候,String
类型数据可以直接被Integer
进行接收了。测试
(1) 强制类型转化this
这个问题的结果咱们已经在上述文章中说起到了,经过反射的方式去进行插入的时候,咱们的数据就会发生错误。spa
若是咱们在一个List<Integer>
中在不知情的状况下插入了一个String
类型的数值,那这种重大错误,咱们该找谁去说呢。操作系统
(2)引用传递问题
上面的问题中,咱们已经说过了T
将在后期被转义成Object
,那咱们对引用也进行一个转化,是否行得通呢?
List<String> listObject = new ArrayList<Object>();
List<Object> listObject = new ArrayList<String>();
复制代码
若是你这样写,在咱们的检查阶段,会报错。可是从逻辑意义上来讲,其实你真的有错吗?
假设说咱们的第一种方案是正确的,那么其实就是将一堆Object
数据存入,而后再由上面所说的强制转化通常,转化成String
类型,听起来彻底ok,由于在List
中原本存储数据的方式就是Object
。但实际上是会出现ClassCastException
的问题,由于Object
是万物的基类,可是强转是为子类向父类准备的措施。
再来假设说咱们的第二种方案是正确的,这个时候,根据上方的数据String
存入,可是有什么意义存在呢?最后都仍是要成Object
的,你还不如就直接是Object
。
其实很简单,若是看过一些公开课想来就见过这样的用法。
public class Part<T extends Parent> {
private T val;
public T getVal() {
return val;
}
public void setVal(T val) {
this.val = val;
}
}
复制代码
相比较于以前的Part
而言,他多了<T extends Parent>
的语句,其实这就是将基类从新规划的操做,就算被编译,虚拟机也会知道将数据转化为Parent
而不是直接用Object
来直接进行替代。
以上就是个人学习成果,若是有什么我没有思考到的地方或是文章内存在错误,欢迎与我分享。
相关文章推荐: