深入java 泛型通配符和上下界限定
2017-09-13 16:15
309 查看
这篇文章总结泛型通配符和上下界限的问题,值得注意的一些细节问题。更多的源代码请访问我的github:https://github.com/yangsheng20080808/deepIntoJava
本文分为2大部分
泛型的继承关系是不变的(引用)
泛型通配符和上下界的限定
Java作为一种面向对象的语言,可以构建层次结构的类型:
在Java里,T类型的字类型可以是T的扩展也可以是T接口的实现。由于自类型是一种可传递的关系,如果A是B的子类,B是C的子类, 那么A也会是C的子类。如上图所示:
每一个 Java类型都是Object的子类型。每一个类型B的子类型A都可以被赋值给B的引用:
Apple a = …; Fruit f = a;
泛型的子类型:
如果一个苹果实例的引用被赋值给了一个Fruit的引用,那么List 和 List之间是什么关系? 谁是谁的子类型,更普遍的说法是,如果类型A是类型B的子类型,那么C和C的关系是什么?
令人惊讶的是,答案是: 没有关系。用更正式的话说,泛型类型的子类型之间关系是不变的。
这意味着下面的代码是不合法的:
List apples = …; List fruits = apples;
下面这个也不合法:
List apples; List fruits = …; apples = fruits;
但是为何会这样呢?难道苹果不是水果么?一盒苹果也不是一盒水果么?在某种程度上讲,答案是肯定的,但是 类型(或者类)把状态和行为封装在了一起,如果一盒苹果是一盒水果会发生什么情况呢?
List apples = …; List fruits = apples; fruits.add(new Strawberry());
如果是的话,我们可以添加其他类型的水果到列表当中,但这一定会被禁止的。有一种更直观的方式是:一盒 水果并不是一盒苹果,因为它可以包含其他类型的水果,比如草莓。
这真的是个问题么?
这不应该是,令Java程序员感到惊讶的是数组和泛型的行为时矛盾的。后者的子类型关系是不变的,而前者的 子类型关系却是协变的:如果A的是B的子类型,那么A[]也是B[]的子类型。
Apple[] apples = …; Fruit[] fruits = apples;
但是等等!也许我们可以把草莓添加到苹果数组当中:
Apple[] apples = new Apple[1]; Fruit[] fruits = apples; fruits[0] = new Strawberry();
代码确实可以编译通过,但是会抛出一个ArrayStoreException的运行时错误。因为对于数字的存储操作, Java 运行时会去检查类型的兼容性,你应该意识到这会有性能损失。
泛型可以安全的使用和改正Java数组的的类型安全弱点。
要是我们想直接往里面添加数据:
我们都知道泛型只是一个语法糖,假如我们硬是要去除类型(不用语法糖)判断:
我就问大家一个问题,大家都能确保我们的子类向上转型成功吗?运行结果是:
所以:仅仅可以接受现有的子类型List 赋值
感谢:http://www.hollischuang.com/archives/230
感谢:http://www.hollischuang.com/archives/255
感谢:http://www.infoq.com/cn/articles/cf-java-generics
本文分为2大部分
泛型的继承关系是不变的(引用)
泛型通配符和上下界的限定
? 通配符类型 <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类 <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object
泛型的继承关系是不变的(引用)
子类型:Java作为一种面向对象的语言,可以构建层次结构的类型:
在Java里,T类型的字类型可以是T的扩展也可以是T接口的实现。由于自类型是一种可传递的关系,如果A是B的子类,B是C的子类, 那么A也会是C的子类。如上图所示:
富士苹果是苹果的字类型 苹果是水果的字类型 富士苹果也是水果的字类型
每一个 Java类型都是Object的子类型。每一个类型B的子类型A都可以被赋值给B的引用:
Apple a = …; Fruit f = a;
泛型的子类型:
如果一个苹果实例的引用被赋值给了一个Fruit的引用,那么List 和 List之间是什么关系? 谁是谁的子类型,更普遍的说法是,如果类型A是类型B的子类型,那么C和C的关系是什么?
令人惊讶的是,答案是: 没有关系。用更正式的话说,泛型类型的子类型之间关系是不变的。
这意味着下面的代码是不合法的:
List apples = …; List fruits = apples;
下面这个也不合法:
List apples; List fruits = …; apples = fruits;
但是为何会这样呢?难道苹果不是水果么?一盒苹果也不是一盒水果么?在某种程度上讲,答案是肯定的,但是 类型(或者类)把状态和行为封装在了一起,如果一盒苹果是一盒水果会发生什么情况呢?
List apples = …; List fruits = apples; fruits.add(new Strawberry());
如果是的话,我们可以添加其他类型的水果到列表当中,但这一定会被禁止的。有一种更直观的方式是:一盒 水果并不是一盒苹果,因为它可以包含其他类型的水果,比如草莓。
这真的是个问题么?
这不应该是,令Java程序员感到惊讶的是数组和泛型的行为时矛盾的。后者的子类型关系是不变的,而前者的 子类型关系却是协变的:如果A的是B的子类型,那么A[]也是B[]的子类型。
Apple[] apples = …; Fruit[] fruits = apples;
协变是什么意思? 维基百科中是这样定义的:在一门程序设计语言的类型系统中,一个类型规则或者类型构造器是: 协变(covariant),如果它保持了子类型序关系≦。该序关系是:子类型≦基类型。 逆变(contravariant),如果它逆转了子类型序关系。 不变(invariant),如果上述两种均不适用。 首先考虑数组类型构造器: 从Animal类型,可以得到Animal[](“animal数组”)。 是否可以把它当作 协变:一个Cat[]也是一个Animal[] 逆变:一个Animal[]也是一个Cat[] 以上二者均不是(不变)
但是等等!也许我们可以把草莓添加到苹果数组当中:
Apple[] apples = new Apple[1]; Fruit[] fruits = apples; fruits[0] = new Strawberry();
代码确实可以编译通过,但是会抛出一个ArrayStoreException的运行时错误。因为对于数字的存储操作, Java 运行时会去检查类型的兼容性,你应该意识到这会有性能损失。
泛型可以安全的使用和改正Java数组的的类型安全弱点。
泛型通配符和上下界的限定
泛型的一般用法上一篇博文都已经详细的讲述了,小节总结泛型通配符的一些使用小结和注意点。(上面的章节没看懂?没关系,看完这一章一样可以看懂。)要是我们想直接往里面添加数据:
List<? extends GrandFaClass> 表示 “具有任何从GrandFaClass继承类型的列表”,编译器无法确定List所持有的类型,所以无法安全的向其中添加对象。可以添加null,因为null 可以表示任何类型。所以List 的add 方法不能添加任何有意义的元素,但是可以接受现有的子类型List 赋值。
我们都知道泛型只是一个语法糖,假如我们硬是要去除类型(不用语法糖)判断:
我就问大家一个问题,大家都能确保我们的子类向上转型成功吗?运行结果是:
所以:仅仅可以接受现有的子类型List 赋值
感谢:http://www.hollischuang.com/archives/230
感谢:http://www.hollischuang.com/archives/255
感谢:http://www.infoq.com/cn/articles/cf-java-generics
相关文章推荐
- 深入理解 Java 泛型:类型擦除、通配符、运行时参数类型获取
- java 泛型之 通配符的限定 之 extends 的实例
- Java基础 通配符?+泛型限定
- java 之 泛型的通配符 和 泛型的限定
- Java泛型类型限定和通配符类型限定
- 牛客网Java刷题知识点之泛型概念的提出、什么是泛型、泛型在集合中的应用、泛型类、泛型方法、泛型接口、泛型限定上限、泛型限定下限、 什么时候使用上限?泛型限定通配符的体现
- JAVA之旅(二十一)——泛型的概述以及使用,泛型类,泛型方法,静态泛型方法,泛型接口,泛型限定,通配符
- 黑马程序员--Java基础加强--06.【泛型通配符限定】【个人总结】
- JAVA之旅(二十一)——泛型的概述以及使用,泛型类,泛型方法,静态泛型方法,泛型接口,泛型限定,通配符
- <转>深入理解 Java 泛型:类型擦除、通配符、运行时参数类型获取
- [转贴] Java 理论与实践: 使用通配符简化泛型使用
- 3.深入java泛型
- Java的泛型中,通配符可以设置上限和下限
- Java 泛型通配符解释
- 在 Java 的泛型类型中使用通配符
- Java 泛型总结(三):通配符的使用
- Java 之泛型通配符 - extends T 与 - super T 解惑
- Java 泛型总结(三):通配符的使用
- Java笔记2 : 泛型的体现,及其上限、下限、通配符
- JAVA集合泛型,类型擦除,类型通配符上限之类的知识点