现在的位置: 首页 > 综合 > 正文

Java 集合类怎么使用

2020年02月19日 综合 ⁄ 共 1997字 ⁄ 字号 评论关闭

  利用 Java 中的动态绑定我们可以实现很多有意思的玩法。例如:

  Object obj = new String();

  集合类想必是小伙伴们低头不见抬头见的类了,那么你有没有想过集合类是否可以这样玩?

  List< Object> list = new ArrayList< Integer>();

  不瞒你说,这是毛毛虫在一次面试中遇到的真实的面试题,当时被问的一脸懵。如果是你,你知道怎么回答吗?

  首先我要告诉你的是,Java 集合类不允许这么玩,这样编译器会直接报错编译不通过。

  Object 和 Integer的关系

  我们都知道赋值一定需要是 is a 的关系,虽然 Object 和 Integer 是父子关系,但是很可惜 List< Object> 和 new ArrayList< Integer> 不是父子关系。当你这么回答时候,面试官脸上露出了狡黠的笑容,假设可以这么玩,那会有什么样的问题呢?

  好吧,假设 Java 允许我们这样写:

  List< Object> objList = new ArrayList< Integer>();

  那么接下来编译器需要来确定一下容器的具体类型,因为容器里面必须存放一种确定的对象类型,不然泛型也就没有诞生的意义了。那究竟是 Object 还是 Integer 呢?

  假设一

  假设它最终确定下来存放的是 Object,那就和如下代码是一样的效果了

  List< Object> list = new ArrayList<>();

  这种写法是 Java 7 引入的写法,官方称之为 diamond ,就是那一对空着的尖括号,使用这种写法时候编译器会自动推算出类型为 Object。这样就相当于赋值语句 ArrayList 中的泛型 Integer 毫无意义,那么无意义的操作 Java 显然是不允许的。

  假设二

  我们再假设最终确定下来的是存放的是 Integer,这样问题就更大了。因为我们都知道 Object 是所有类的基类,这就代表着 objList 可以添加任何类型的对象。即我们可以做如下操作

  objList.add(new String());objList.add(new Integer());

  然后我们使用容器里面的对象的时候取出来需要将它强制转换为 Integer 对象,如果容器中本来就存放的是 Integer 的对象还好,如若不是就会出现 ClassCastException。有没有发现,这样不是恰如回退到没有泛型的 Java 版本了,即 Java 1.5 之前。我们使用容器不能在编译期间保证它的类型安全了,历史回退这种傻傻的操作Java 也绝对是不允许的。

  经过如上的一通分析我们发现泛型它就是不能这么玩,而且这么玩也是毫无意义。

  这个时候面试官微微的点点头,表示略有赞同,你以为终于可以结束这样各种假设的骚操作了。面试官嘴角再次漏出狡黠的笑容,前面你说 List< Object> 和 ArrayList< Integer> 不是父子关系,那泛型里面有父子关系吗或者说 List< Object> 和 ArrayList< Integer> 是否存在着某种关系?

  好吧。它们是兄弟,都是 List< ?> 的子类。也就是说,你可以这么玩:

  List< ?> list1 = new ArrayList< Object>();List< ?> list2 = new ArrayList< Integer>();

  如上是 2 段代码是合法的,容器造出来了。那让我们来给容器里面塞一些对象进去吧。

  list1.add(new Object());

  不好意思你不能这么写,因为这 2 个容器里面可以存储任何东西,导致编译器都无法判断到底给里面会存储什么,它俩是 Read Only 哦。What? 那这有啥用呢?

  好吧,假如有这样一个需求:写一个方法可以累加数字容器里面的元素值,那我们可以这样去写

  public static long sumNumbers(List< ?> list) { Long sum = 0l; for (Object num : list) { sum += ((Number) num).longValue(); } return sum;}

  你可能注意到了,没错这里经历了一次强制类型转换,万一方法的调用方传入的是 new ArrayList< String>() 那么此处你可能会接到一个 ClassCastException 了。那有没有好的解决办法呢?有!

  public static long sumNumbers(List< ? extends Number> list) {/****/}

  我们将方法的参数改为了 List list ,这样当调用方试图传入非 Number 类型的容器实例时候在编译期就会直接报错咯,嘿嘿。

  当然啦,你也可以通过 super 关键字来设置泛型参数的下限,例如 List< ? super Number> list ,那这个时候调用方法的时候就只能传入泛型参数是 Number 或者 Number 的父亲级别的容器了。

抱歉!评论已关闭.