Java数组协变与范型不变性的案例?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!
变性是OOP语言不变的大坑,Java的数组协变就是其中的一口老坑。因为最近踩到了,便做一个记录。顺便也提一下范型的变性。
解释数组协变之前,先明确三个相关的概念,协变,不变和逆变。
<强> 强>
假设,我为一家餐馆写了这样一段代码
类Soup{ 公共空间添加(T T) {} } 类蔬菜{} 类胡萝卜延伸蔬菜{}
那么问题来了,Soup
第一反应,Soup
Soup汤=new Soup (); 汤。添加(新番茄());
第一句没问题,Soup
但是,两句放在一起却有了问题。汤的实际类型是Soup
那么,Soup
(1)如果Soup
(2)如果Soup
(3)如果Soup
理解了协变,不变和逆变的概念,再看Java的实现. Java的一般泛型是不变的,也就是说Soup
<强> 强>
Java中,数组是基本类型,不是泛型,不存在Array
与泛型的不变性不同,Java的数组是<强>协变强>的。也就是说,胡萝卜[][]是蔬菜的子类。而上一节中的例子已经表明,协变有时会引发问题。比如下面这段代码
蔬菜蔬菜[]=new胡萝卜[10]; 番茄蔬菜[0]=new ();//运行期错误
因为数组是协变的,编译器允许把胡萝卜[10]赋值给蔬菜[]类型的变量,所以这段代码可以顺利通过编译。只有在运行期,JVM真的试图往一堆胡萝卜中插入一个西红柿的时候,才发现大事不好,所以,上面的代码在运行期会抛出一个. lang。ArrayStoreException类型的异常。
数组协变性,是Java的著名历史包袱之一。使用数组时,千万要小心!
如果把例子中的数组替换为列表,情况就不同了。就像这样
ArrayList蔬菜=new ArrayList ();//编译期错误 蔬菜。添加(新番茄());
ArrayList是一个泛型类,它是不变的,所以,ArrayList
两段代码虽然都会报的错,但通常情况下,编译期错误总比运行期错误好处理一些。
<强> 强>
泛型是不变的,但某些场景里我们还是希望它能协变起来。比如,有一个天天喝蔬菜汤减肥的小姐姐
类女孩{ 公共空饮料(Soup汤){} }
我们希望喝方法可以接受各种不同的蔬菜汤,包括Soup
要实现这一点,应该采用一种类似于协变性的写法
公共空饮料(Soup<?Vegetable>延伸;汤){}
意思是,参数汤的类型是泛型类Soup
但是,这种方法有一个限制。编译器只知道泛型参数是蔬菜的子类,却不知道它具体是什么,所以,所有非零的泛型类型参数均被视为不安全的。说起来很拗口,其实很简单。直接上代码