AHK程序设计秒懂Java

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

2019-01-04  本文已影响1人  d61f25068828

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,得到许可并标明出处和原链接后方可转载。未经授权,禁止转载。版权所有 ©心如止水 保留一切权利。

心如止水
上一篇 下一篇

猜你喜欢

热点阅读