” 中的 < E>?...">
美文网首页AHK程序设计秒懂Java
[Java菜鸟系列] 红警思维看"泛型"

[Java菜鸟系列] 红警思维看"泛型"

作者: d61f25068828 | 来源:发表于2019-01-04 13:35 被阅读1次

    J005-[Java菜鸟系列] 红警思维看"泛型"

    菜鸟:如何理解 “ArrayList<E>” 中的 < E>?最近在学习 Java 编程,查看 Library 的时候,总是能看到以 结尾的参数。请问这是什么啊?具体如何使用?

    美军的困境:多功能战车陷阱

    在越南战争中,美军需要同时应对苏军航空兵/越共游击队,也需要承担坦克维护任务。

    五角大楼的第一套方案是,订购三种战车,分别用于防空/对地攻击/维修;第二套方案是订购一种多功能战车。

    第一种方案简单,但容易浪费,而且不灵活;最后决定用第二种方案,它可以根据实际情况改变用途。

    多功能步兵战车

    但战场上却出现了坑队友事件:粗心的大兵把攻击战车用于坦克维护,导致坦克被击毁。

    Error:坑队友

    不能忍!美利坚再有钱也经不起这么折腾。

    大兵们想出了一个办法,在多功能战车用于坦克维护前,让工程师进入战车。这样就不会操作失误了。

    安全操作

    Java的困境:Object类型陷阱

    背景:ArrayList的实际需求

    在Java中我们用ArrayList存储对象,因为它非常的方便。这就相当于美国大兵的战车。

    为了满足的对象存储需求,有两种方案。

    第一种:为每个对象设计一个不同的ArrayList类。第二:设计通用ArrayList类。

    因为对象的种类多种多样,怎么可能每一个都设计一个相同的ArrayList呢?

    所以,Java的设计者和美国大兵一样,选择了"通用方案"。

    坑队友:通用方案的弊端

    如何通用?最简单办法就是用Object来设计ArrayList。

    Object是根类,可以容纳一切对象,所以"通用化"就实现了。

    //示例-J005-1:最初的设计
    public class ArrayList {
    
    private Object[] elementData;
    public Object get(int i) { . . . }
    public void add(Object o) { . . . } 
    
    }
    

    但是,这种方法同样会造成"坑队友"。

    假设我们要使用ArrayList装String对象(stringList),当我们获取值的时候,其实获取的东西是Object,所以我们必须手动转为String。(强制转型)

    String s = (String) stringList.get(0);

    当你满怀信心的运行的时候,结果却会是这样的

    Exception in thread "main" java.lang.ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.String
    
    ???

    为什么会出现这种情况呢?

    因为,向stringList中添加元素的时候,使用的是public void add(Object o) { . . . }方法,实际上你可以往里面添加任何东西。

    所以前面你不小心添加了一个StringBuffer,呃,就这么直接挂掉了。

    遇到这种事情的心情,emmm.... 你再看看这张图就懂了。

    Error:坑队友 内心崩溃

    像美国大兵学习

    这件事情上,我们需要向美国大兵学习,先回顾一下美国大兵是怎么做的。

    大兵们在使用多功能战车的时候,要求专业人士先进入,以确保战车的安全。

    这样答案就很明确了,在实例化ArrayList之前,我们先传入一个"特殊参数",以确保安全就行了。

    安全操作

    我们该怎么样传入这个参数呢?

    仔细想一想,虽然{}()都被占了,好在还剩下一个框框<>没用,那么就用这个了。

    这个参数就被称为"类型参数"。

    在设计泛型类的时候,我们用T来指代还未被确定的类型

    当然你也可以用其他的字母,这只是一个习惯。

    <T>代表Type,代指一切类型。其实在ArrayList等 集合类 中一般会使用<E>,也就是Element的意思。

    //示例-J005-2:改进后的设计
    public class ArrayList<T> {
    private T[] elementData;
    public T get(int i) { . . . }
    public void add(T o) { . . . } 
    }
    

    只要我们在实例化之前,先指定其类型,就可以避免"坑队友"事件的发生了。

    image

    为什么new ArrayList<>中的泛型参数可以被省略呢?

    构造对象之后,对象被传递给ArrayList<String>类型的变量,那么构造器的泛型参数自然也就是String。

    在Java SE 7 之后,如果泛型参数可被推断,那么就可省略。

    总结:泛型在效率和安全之间找到了平衡

    1. 泛型诞生的背景还是懒。诸如ArrayList这样的类,并不需要为每个对象都设计一个,完全是可以通用的。
    2. 通用设计产生了安全性问题。一开始人们用Object类型建立ArrayList类,但是这种方法很容易"坑队友",造成ClassCastException。使用起来非常不安全。
    3. 在安全和效率之间找到了平衡。为了在通用的基础上保证安全性,增加了一个新的参数,叫做"类型参数"。

    可以说,"泛型"的本质就是新增了一个叫做"类型参数"<>的东西。

    顺便提一句"兼容性"

    泛型是Java1.5才加入的,不可避免的有"兼容性"问题。

    为了向后兼容,"泛型"只存在于"编译器",不存在于"虚拟机"。

    所以,只要程序运行起来,就没有"泛型"这个概念了,这被称为"类型擦除"。

    也有人把这称为"Java的伪泛型",我们就不用管了,一个名字而已,不用争来争去的。

    "类型擦除"后,泛型类露出其本来面目了,这个本来面目就叫做"原始类型"。

    如果你什么都没动的话,比如你设计的类是<T>泛型类型,那么本来面目就是Object,但是你也可以限定一下,比如<T extends Comparable>,那么本来面目就是Comparable。

    其实,了解了"泛型"产生的历史背景和其本质,这些细节都好理解。

    End

    鲁迅镇楼

    鲁迅

    **心如止水是Java/AHK持续学习者,欢迎您来和我探讨Java/AHK问题 _ **

    更多文章:

    版权声明:

    该文章版权系“心如止水”所有,欢迎分享、转发,但如需转载,请联系QQ:3404624865,得到许可并标明出处和原链接后方可转载。未经授权,禁止转载。版权所有 ©心如止水 保留一切权利。

    心如止水

    相关文章

      网友评论

        本文标题:[Java菜鸟系列] 红警思维看"泛型"

        本文链接:https://www.haomeiwen.com/subject/xvbhrqtx.html