为什么Java不允许创建范型数组
问题示例
List<Integer>[] intListArr = new ArrayList<Integer>[8]; // 编译时报错
能看到这么看似没啥问题的一个简单语句甚至连编译都不会通过,为了能理解这里面的缘由,我们先得了解变型(Variant)的概念
变型(variant)
- 协变(covariant) 允许在子类出现的地方用超类替换(A feature which allows to substitute a subtype with supertype.)
- 逆变(contravariant) 允许在超类出现的地方用子类替换(A feature which allows to substitute a supertype with subtype.)
- 不变(invariant) 以上两种都不适用。
数组中的变型
早期版本的Java不包含泛型(generics,即参数化多态)。在这样的设置下,使数组为“不变”将导致许多有用的多态程序被排除。
例如,考虑一个用于重排(shuffle)数组的函数,或者测试两个数组相等的函数,使用Object的equals方法. 函数的实现并不依赖于数组元素的确切类型,因此可以写一个单独的实现而适用于所有的数组:
boolean equalArrays (Object[] a1, Object[] a2);
void shuffleArray(Object[] a);
然而,如果数组类型被处理为“不变”,那么它仅能用于确切为Object[]类型的数组。对于字符串数组等就不能做重排操作了。
所以,Java把数组类型处理为协变。在Java中,String[]是Object[]的子类型。
范型中的变型
当范型最初在jdk1.5被引入的时候,是有意没有把它设计成协变的,我们来看个例子:
List<Dog> dogs = new ArrayList<Dog>(); // Dog的超类是Animal
List<Animal> animals = dogs; // 把dogs的引用赋给animals超类,编译不通过
animals.add(new Cat()); // Cat是超类Animal的另一个实现
Dog dog = dogs.get(0); // 看起来这应该是对的?^^
即使jdk团队修改底层实现让这段程序能通过编译,最终的结果肯定是ClassCastException,这时候我们再来看看范型设计的目的:
Generics add stability to your code by making more of your bugs detectable at compile time.
范型希望能在编译期间就能找到并避免程序潜在的bug,所以根本就不会把范型实现为协变,这样会更加容易出错,也就是我们在很多书中看到对本问题的解释:这就违背了 Java 泛型的设计原则。
可能有人会问,通配符不是让范型可以协变或逆变或不变吗?
补充说明:
通配符的能力是可以通过指定一个更严格的界来让范型变得更加特化,使范型可以在上界协变(List<? extends Cat> 是 List<? extends Animal>的子类型)以及在下界逆变。这是一种范型定义的描述,并不是实际使用时对范型的类型实例化,所以通配符丰富了范型的功能,和范型本身是不变(invariant)的特性不冲突。
结论
说了这么多,其实就是想说明一种设计的初衷,数组是协变的,范型是不变的,这两者结合在一起使用那就肯定会出问题,例如:
List<Integer>[] intListArr = new ArrayList<Integer>[8];// 假设范型数组创建成功
List<Long> longList = new ArrayList<Long>();
List<Number>[] numberArr = intListArr; // 协变性保证可以赋值
numberArr[0] = longList;
Integer value = numberArr[0].get(0);
结果就是ClassCastException了。
当然如果代码写成
new ArrayList[8]编译是通过了,这时候范型是对变量intListArr的类型说明,而不是创建范型数组了。
参考
- wikipedia Covariance_and_contravariance
- javase tutorial generics
- StackOverflow: Why are arrays covariant but generics are invariant?
为什么Java不允许创建范型数组的更多相关文章
- Java创建boolean型数组
Java如何声明并初始化一个boolean型的数组? public class Main{ static boolean[] arr1 = new boolean[20]; public static ...
- Java数据结构与算法分析-第一章(引论)-Java中的范型<T,E>构件
一.为什么需要使用范型? 官方的说法是:Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质 ...
- Java 中能创建 volatile 数组吗?
能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不 是整个数组.我的意思是,如果改变引用指向的数组,将会受到 volatile 的保护, 但是如果多个线程同时改变数 ...
- C# 利用范型与扩展方法重构代码
在一些C#代码中常常可以看到 //An Simple Example By Ray Linn class CarCollection :ICollection { IList list; public ...
- Java数组协变与范型不变性
变性是OOP语言不变的大坑,Java的数组协变就是其中的一口老坑.因为最近踩到了,便做一个记录.顺便也提一下范型的变性. 解释数组协变之前,先明确三个相关的概念,协变.不变和逆变. 一.协变.不变.逆 ...
- Java范型学习笔记
对于范型的使用或者说印象只有集合,其他地方即使使用过也不知道,反正就是只停留在List<E> Map<K, V>,最近刚好闲来无事,就找找资料学习一下:下列为个人学习总结,欢迎 ...
- Java范型随笔
最近在帝都好无聊啊, 排遣寂寞就只有让自己不要停下来,不断的思考了 QWQ; 最近做ndk, java有点忘了,突然看到了一些java范型方面的问题, 踌躇了一会, 想着想着,决定还是写个随笔记录下来 ...
- Java Comparator的范型类型推导问题
问题 在项目中,有一处地方需要对日期区间进行排序 我需要以日期区间的开始日为第一优先级,结束日为第二优先级进行排序 代码 我当时写的代码如下: List<Pair<LocalDate, L ...
- Java范型
泛型不用考虑对象的具体类型.优点在于,因为不用考虑对象的具体类型所以可以对一类对象执行一定的相同操作:缺点在于,因为没有考虑对象的具体类型所以就不能使用对象自带的接口函数.泛型的最佳用同是实现容器类. ...
随机推荐
- Android面试题收录及解答10月刊
前言 嗨,大家好,好久不见.这里跟大家侃侃这中间发生了什么. 一个月前呢,想准备面试,就网上随便找找面试题什么的,发现要么就是卖课的,要么就是不给详细回答的或者回答不够深的(也许是我没找到).反正稍微 ...
- 栈&队列&并查集&哈希表(julyedu网课整理)
date: 2018-11-25 08:31:30 updated: 2018-11-25 08:31:30 栈&队列&并查集&哈希表(julyedu网课整理) 栈和队列 1. ...
- DLL转存为IL文件及修改后重新生成DLL文件
DLL反编译工具有:dotPeek (jetbrains) .ILSpy DLL转存IL使用:ildasm 打开DLL文件,选择file->dump. il .res重新生成DLL工具: ila ...
- 对精致码农大佬的 [理解 volatile 关键字] 文章结论的思考和寻找真相
一:背景 1. 讲故事 昨天在园里的编辑头条看到 精致码农大佬 写的一篇题为:[C#.NET 拾遗补漏]10:理解 volatile 关键字 (https://www.cnblogs.com/will ...
- Luogu P6280 [USACO20OPEN]Exercise G
题意 定义一个长度为 \(n\) 的置换的步数为将 \(P=(1,2,\cdots,n)\) 在该置换操作下变回原样的最小次数. 求所有 \(K\) 的和,使得存在一个长度为 \(n\) 的置换使得其 ...
- 测试可变字符序列stringBuilder
测试可变字符序列 stringBuilder(线程不安全,效率高,一般用),stringBuffer(线程安全,效率低)stringBuilder,stringBuffer和string的区别前者可变 ...
- 微信小程序-游记分享(无后台)
游记分享 博客班级 https://edu.cnblogs.com/campus/zjcsxy/SE2020 作业要求 https://edu.cnblogs.com/campus/zjcsxy/SE ...
- Git clone 克隆Github上的仓库,速度慢?
一. 终端输入: git config --global http.postBuffer 524288000 二. git替换https 三. windows 安装 https://motrix.ap ...
- Docker(8)- docker search 命令详解
如果你还想从头学起 Docker,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1870863.html 作用 从 Docker Hub ...
- 线程队列分享ppt