疯狂Java讲义读书笔记09 Java泛型
本章的知识可以与前一章的内容补充阅读,因为JDK1.5增加泛型支持很大程度上都是为了记住其元素的数据类型。
在没有泛型之前,一旦将一个对象丢进Java集合中,集合就会忘记对象的类型,把所有的对象当成Object类处理,当程序从集合中取出对象后,就需要进行强制转换,这种强制转换使得代码臃肿,而且很容易引起ClassCastException
增加了泛型这种集合,完全可以记住集合中元素的类型,并且可以在编译时检查元素的类型,如果试图向集合中添加不满足类型要求的对象,编译器就会提示错误。
Java集合有一个缺点,就是把一个对象丢进集合里之后,集合就会忘记这个对象的数据类型
当再次取出这个对象时,该对象的编译类型就变成了Object类型(其运行时类型没变)
Java集合之所以被设计成这样,是因为集合的设计者不知道我妈会用集合来保存什么类型的对象,所以它们把集合设计成能保存任何类型的对象,这样会有很好的通用性。
不使用泛型会导致将不同类的实例丢进同一个集合里
所谓泛型就是允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态指定。
包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参,从而可以动态地生成无数多个逻辑上的子类,但这种子类在物理上并不存在。
可以为任何类、接口增加泛型声明(并不是只有集合类可以,虽然集合类是泛型的重要使用场所)
当创建带泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名。而不要增加泛型声明。例如:为Apple<T>类定义构造器,其构造器依然是Apple,而不是Apple<T>。调用该构造器时却可以使用Apple<T>的形式,当然应该为T形参传入实际的类型参数。Java7提供了菱形语法,允许省略<>中的类型实参。
当创建了带泛型声明的接口、父类之后,可以为接口创建实现类,或从该父类派生子类,需要指出的是,当使用这些接口父类时,不能再包含类型形参,所以下面代码是错误的。

如果想从Apple类派生一个子类可以改成如下写法

或者不传入类型参数

如果是从Apple<String>类派生子类,则在Apple类中所有使用T类型形参的地方都会被替换成String类型
如果没有传入类型参数,会发出安全警告,都当成Object类处理。
不管泛型类型形参传入哪种类型实参,对Java来说,都是同一个类处理,在内存中也只占用一块内存空间,因此在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用类型形参。
系统中并不会产生真正的泛型类,所以instanceof运算符后不能使用泛型类。例如下面代码是错误的

类型通配符
正如前面讲的,当使用一个泛型类时。都应该为这个泛型类传入一个类型实参,如果没有传入类型实际参数,编译器就会提出泛型警告。
假设现在需要定义一个方法,该方法里有一个集合形参,集合形参的元素类型不确定,该怎么定义呢?
List<String>对象不能被当成List<Object>对象使用,也就是说,List<String>类并不是List<Object>类的子类。
Java泛型的设计原则是,只要代码在编译时没有出现警告,就不会遇到运行时ClassCastException异常
使用类型通配符
类型通配符是?
写作List<?>意思是元素类型未知的List

现在使用任何类型的List来调用它,程序依然可以访问集合c中的元素,其类型是Object,这永远是安全的因为不管List的真实类型是什么,它包含的都是Object
但这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素加入其中。例如,如下代码将会引起编译错误

设定类型通配符的上限
当直接使用List<?>这种形式时候,表明这个List集合可以是任何泛型List的父类。但还有一种特殊的情况,程序不希望这个List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类。
一个抽象类Shape

 
 
上面方法的形参类型是List<Shape>,而List<Circle>并不是List<Shape>的子类型,因此,下面代码将引起编译错误。

所以需要一种泛型表示方法,它可以表示所有Shape泛型List的父类。为了满足这种要求,Java泛型提供了被限制的泛型通配符

Java泛型不仅允许在使用通配符形参时设定上限,而且可以在定义类型形参时设定上限。
 
泛型方法
前面介绍了在定义类,接口的时候可以使用类型参数,在该类的方法定义和成员变量定义、接口的方法定义中,这些类型形参可被当成普通类型来用。
定义泛型方法

Java不允许把对象放进一个未知类型的集合中

到底何时使用泛型方法何时使用通配符
如果某个方法中一个形参A的类型或返回值的类型依赖于另一个形参B的类型,则形参B的类型声明不应该使用通配符。
java7的菱形语法与泛型构造器
Java也允许设定通配符的下限
<? super Type>
擦除和转换
在严格的泛型代码里面,带泛型声明的类总应该带着类型参数,但为了与老的Java代码保持一致,也允许在使用带泛型声明的类时不指定实际的类型参数。如果没有为这个泛型指定实际的类型参数,则该类型参数被称为raw type原始类型,默认是声明该类型参数时指定的第一个上限类型
当把一个具有泛型信息的对象复制给另一个没有泛型信息的变量时,所有在尖括号之间的类型信息都将被扔掉。比如一个List<String>类型被转换为List,则该List对集合元素的类型检查变成了类型参数的上限即Object。
从逻辑上看如果直接将一个List对象赋值给一个List<String>对象应该引起编译错误,但实际上并不会。对泛型而言,可以直接将一个List对象赋值给List<String>对象,编译器仅仅提示未经检查的转换。
泛型与数组
Java泛型有一个很重要的设计原则,如果一段代码在编译时没有提出未经检查的转换警告,则程序在运行时不会引发ClassCastException异常。正是基于这个原因,所以数组元素类型不能包含类型变量或类型形参,除非是无上限的类型通配符。
但可以声明元素类型包含类型变量或类型形参的数组
所以Java不支持创建泛型数组

                
            
        
浙公网安备 33010602011771号