为什么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范型
泛型不用考虑对象的具体类型.优点在于,因为不用考虑对象的具体类型所以可以对一类对象执行一定的相同操作:缺点在于,因为没有考虑对象的具体类型所以就不能使用对象自带的接口函数.泛型的最佳用同是实现容器类. ...
随机推荐
- 数据恢复软件推荐-easyrecovery绿色破解版(附注册码)免费下载
easyrecovery破解版专注于PC端存储数据的抢救恢复,软件的整体界面风格和360杀毒有些许相似,没有看起来像牛皮藓的杂乱广告,只有六个功能按键,对应你所遇到的数据丢失状况级别,点击最为适合的功 ...
- jdk1.8特性2
public class User { private Long id; private String userName; private String roleName; private Strin ...
- D. 停不下来的团长奥尔加 动态规划
题目描述 分析 设\(f[i]\) 为从 \(i\) 走到 \(i+1\) 的步数 初始值 \(f[i]=2\) 则 \(f[i]=\sum_{i=p[i]}^{i}f[i]\) 考试的时候用树状数组 ...
- NB-IoT的数据链路层和上行传输信道类型
NB-IoT的数据链路层 NB-IoT在LTE系统的基础上对数据链路层进行了大量简化,但整体上还是保持了原有的框架.数据链路层是二层协议,包含了3个子层:MAC子层.RLC子层和PDCP子层. 以网络 ...
- 4G DTU是什么 可以应用于哪些行业?
4G是什么? 4G是移动电话网络通过蜂窝塔传输的信号的名称,蜂窝塔连接到更宽的互联网.这些是当今智能手机使用的信号,当您外出时,可以通过手机上网,因此他们不依赖电缆或光纤,也就是说无线网. 使用合适的 ...
- 自定义控件-只有横线的文本输入框(TxtLine)
需求:在实际开发中,由于TextBox控件的背景色样式不美观,且TextBox不能直接设置背景颜色,因此需要使用自定义控件以实现如下效果 实现代码 public partial class TxtLi ...
- Luogu P2024 [NOI2001]食物链
并查集 首先先要读懂题目,a是b的食物的话,b的天敌是a,b的食物是a的天敌 比如,人吃鸡,鸡吃草,那么草吃人..... 所以建3个并查集,+n时表示这是其食物,+2*n时表示这是其天敌 所以当x,y ...
- ubuntu下安装nginx -php
mysql : sudo apt-get install mysql-server mysql-client nginx: sudo apt-get install nginx安装Nginx稳定版本 ...
- EFCore自己用的点东西
测试一下EF core 的操作 蛀牙总是用导航属性有的时候有点不理解 写个例子给自己用 class Program { static void Main(string[] args) { MyDbCo ...
- javaweb项目中jsp的from表单提交action内容与web.xml的servlet-mapping对应
login.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> ...