Java基础:泛型及其擦除性、不可协变性
来源:互联网 发布:水泥自流平环保吗 知乎 编辑:程序博客网 时间:2024/06/05 11:45
摘自http://blog.csdn.net/jiyiqinlovexx/article/details/46675881
1泛型语法:
泛型类: class ClassName<T>{}
泛型方法:public <T> void f(T x){}
基本指导原则:如果使用泛型方法可以取代将整个类泛型化,那么就应该使用泛型方法,因为它可以让事情更加清楚。
2为什么使用泛型?
在Java SE1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是:
(1)在编译时检查类型安全;
(2)并且所有的强制转换都是自动和隐式的,提高代码的重用率。
促成泛型出现最引人注目的一个原因就是为了创造容器类。
优先考虑泛型!!!
使用泛型比使用需要在客户端代码中进行转换的类型来的更加安全,也更加容易。
在设计新类型的时候,要确保它们不需要这种转换就可以使用。这通常意味着要把类做成是泛型的。
比如
客户端代码使用Stack的时候,如果不使用泛型,在取用Stack中的对象时还需要进行不安全的类型判断,类型转换等,而且代码还比较复杂。如果使用带泛型的Stack,客户端代码中就不需要进行类型转换了,直接使用而且不会复杂。
3泛型数组:
无法创建泛型数组,一般的解决方式是在任何想要使用泛型数组的地方使用ArrayList。
- public <T> voidtest()
- {
- //Cannotcreate a generic array of T
- T[]tList = new T[10];
- int[]iList = new int[10];
- //useArrayList<T>
- ArrayList<T>sList = new ArrayList<T>();
- }
4类型擦除:
一、概念
类型擦除:将泛型类型中类型参数信息去除,只映射为一份字节码,在必要时进行类型检查和类型转换。
编译时通过两个步骤来对泛型类型的类型进行擦除:
1.将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。
2.移除所有的类型参数。
备注:擦除也是为什么泛型必须编译时进行类型检查的原因,因为运行时类型信息被擦除了。
二、擦除举例
(1)容器泛型类型擦除:
- ArrayList<Integer>l1 = new ArrayList<Integer>();
- ArrayList<String> l2= new ArrayList<String>();
- LinkedList<Integer>l3 = new LinkedList<Integer>();
- List<String> l4 =new LinkedList<String>();
- System.out.println(l1.getClass().getName());
- System.out.println(l2.getClass().getName());
- System.out.println(l3.getClass().getName());
- System.out.println(l4.getClass().getName());
- //output
- java.util.ArrayList
- java.util.ArrayList
- java.util.LinkedList
- java.util.LinkedList
(2)自定义泛型类型擦除:
再看一个自定义的泛型类:
- class TObject<T>
- {
- privateT obj;
- publicvoid Set(T object)
- {
- this.obj= object;
- System.out.println("T:"+object.getClass().getName());
- }
- }
- class TObject
- {
- privateObject obj;
- publicvoid Set(Object object)
- {
- this.obj= object;
- }
- }
(3)自定义继承关系泛型类型擦除:
- class Manipulator<Textends SuperClass>
- {
- private T obj;
- public Manipulator(T x){
- obj = x;
- }
- public void doSomething(){
- obj.f();
- System.out.println(obj.getClass().getName());
- }
- }
- class Manipulator
- {
- private SuperClass obj;
- public Manipulator(SuperClass x){
- obj = x;
- }
- public void doSomething(){
- obj.f();
- System.out.println(obj.getClass().getName());
- }
- }
三、擦除原因:
泛型不是在java一开始就有的,擦除是java中泛型实现的一种折中手段。
具体来说两个原因使得泛型代码需要类型擦除:
(1)引入泛型代码不能对现有代码类库产生影响,所以需要将泛型代码擦除为非泛型代码;
(2)当泛型代码被当做类库使用时,为了兼容性,不需要知道泛型代码是否使用泛型,所以需要擦除;
5不可协变:
(1)数组和泛型对比
数组是可协变的、泛型是不可协变的。
什么是可协变性?举个例子说明:
数组可协变(covariant)是指如果类Base是类Sub的基类,那么Base[]就是Sub[]的基类。
泛型不可变的(invariant)是指List<Base>不会是List<Sub>的基类,两者压根没关系。
(2)泛型为什么不可协变
泛型“编译时进行类型检查(类型安全)”特性决定了其不可协变。
ArrayList<Object> objList = new ArrayList<Long>();
//can't compile pass
类型安全检查
Object[] objArray = new Long[10];
//compile OK
如果ArrayList<Long>类型对象可以赋值给ArrayList<Object>类型引用,那么就违反了泛型类型安全的原则。
因为如果编译器你这样做,会导致可以往容器中放置非Long型的对象。但是数组就无所谓,他不是类型安全的。
再看看下面代码,第一行编译错误是因为不可协变性,那么为什么第二行可以呢?
List<Type> listt = new ArrayList<SubType>();
//can't compile pass
List<? extends Type> listt = new ArrayList<SubType>();
//OK
参考泛型通配符,这就是其作用
不可协变并不代表不能在泛型代码中将父类出现的地方使用子类代替,如下面代码是合法的:
ArrayList<Type> list = new ArrayList<Type>();
//Type is SuperClass
list.add(new SubType());
//SubType is SubClass
Type[] tt = new Type[3];
tt[0] = new SubType();
(3)数组可协变带来的问题:
数组的协变性可能会导致一些错误,比如下面的代码:
- public static voidmain(String[] args) {
- Object[] array = new String[10];
- array[0] = 10;
- }
Exception in thread"main" java.lang.ArrayStoreException: java.lang.Integer
但是对于泛型就不会出现这种情况了:
- public static voidmain(String[] args) {
- List< Object> list = newArrayList< String>();
- list.add(10);
- }
6通配符:
通配符在类型系统中的作用部分来自其不会发生协变(covariant)这一特性。即通配符产生一部分原因来自突破不可协变的限制。
可以认为通配符使得List<?>是List<AnyType>的基类,List<? extends Type>是List<SubType>的基类。
- // collection1可以存放任何类型
- Collection<?>collection1 = new ArrayList<String>();
- collection1 = newArrayList<Integer>();
- collection1 = newArrayList<Object>();
- //collection3表示它可以存放Number或Number的子类
- Collection<?extends Number> collection3 = null;
- collection3 = newArrayList<Number>();
- collection3 = newArrayList<Double>();
- collection3 = newArrayList<Long>();
- //collection4表示它可以存放Integer或Integer的父类
- Collection<? superInteger> collection4 = null;
- collection4 = newArrayList<Object>();
- Java基础:泛型及其擦除性、不可协变性
- Java基础:泛型及其擦除性、不可协变性
- Java基础:泛型及其擦除性、不可协变性
- Java中的String的 方法归类 及其 不可变性
- 数组的协变性与范型的不可变性
- 泛型及其擦除
- Java基础:String不可变性和final修饰
- 图解Java String不可变性
- Java 协变性 逆变性
- JAVA泛型擦除
- Java泛型擦除
- Java泛型擦除
- Java泛型擦除
- Java泛型擦除
- Java泛型擦除
- JAVA泛型擦除
- Java 泛型擦除
- java--泛型擦除
- ANDROID NDK使用第三方静态库的方法
- 【iOS开发系列】字典数组按key分组和排序
- 织梦常用标签
- 在子类中调用父类的带参数的构造方法
- 配置好jdk之后eclipse打开报错以及VPN上外网问题
- Java基础:泛型及其擦除性、不可协变性
- Algorithms—171.Excel Sheet Column Number
- Rectangle Area
- iOS 如何在一个app中调用另一个app
- 慎用ArrayList的contains方法,使用HashSet的contains方法代替
- mybatis中自定义1对1的resultMap
- 自学笔记-C语言复习2015年7月1日
- UIImagePickerController 状态栏颜色改变,自定义返回按钮
- SMP学习笔记之使用REST API在任何操作系统上消费SMP的OData服务