***概述
泛型实现了参数化类型的概念,使代码可以应用于多种类型。泛型的最初目的是为了使类或方法具有最广泛的表达能力,这点可以通过解耦类或方法与所使用的类型之间的约束来实现。在创建参数化类型的一个实例时,编译器为你负责转型操作,并且保证类型的正确性。
***简单泛型
许多原因促使泛型的出现,最显著的一个是为了创造容器类。有时,需要能同时持有多个对象。但通常只会使用容器来存储一种类型的对象。泛型的主要目的之一就是指定容器要持有的对象类型,有编译器来保证类型的正确性。
***元组
一次方法调用就能返回多个对象,经常会需要类似的功能。解决办法是创建一个对象,用它来持有想要返回的多个对象。元组是将一组对象直接打包存储与其中的一个单一对象,这个容器对象内元素是只读的。
***泛型接口
泛型可以用于接口,和类使用方法相同。java的泛型局限:基本类型无法作为类型参数。
***泛型方法
是否拥有泛型方法,与其所在的类是否为泛型没有关系。泛型方法使得该方法能够独立于类而产生变化。应该尽量使用泛型方法而不是泛型类,这样更容易明白。
定义泛型方法,只需将泛型参数列表置于返回值之前:
<T> void kk(T t){
System. out .println(t);
}
使用泛型类时,必须在创建对象时指定类型参数的值,而泛型方法则不用指定,编译器会找出具体类型(类型参数推断)。使用泛型方法时和使用普通方法一样,如果调用kk()时传入基本类型,自动打包机制就会介入,将基本类型的值包装为对应的对象。泛型方法和自动打包机制避免了以前我们不得不写的代码。类型推断只对赋值操作有效,其他时候并不起作用。
在泛型方法中,可以显式地指明类型。 f.<Person>getPerson()
***类型信息丢失
在泛型代码内部,无法获得任何有关泛型参数类型的信息。java泛型是使用擦除来实现的,在使用泛型时,任何具体的类型信息都被擦除,仅知道正在使用一个对象。List<String>和List<Integer>在运行时事实上是相同的类型。
只有当你希望使用的类型参数比某个具体类型(以及它的所有子类型)更加泛化时(希望代码能够跨多个类工作),使用泛型才有所帮助。
***创建数组
在泛型中创建数组,推荐使用Array.newInstance()方式。
不能创建泛型数组,解决方案是使用ArrayList.将继续获得数组的行为,以及由泛型提供的编译期的类型安全。成功创建泛型数组的唯一方式是创建一个被擦除类型的新数组,然后对其转型。因为有了擦除,数组的运行时类型就只能是Object[]。
为了弥补擦除,可以在构造器中传递一个类型标记,以便从擦除中恢复,使得我们可以创建需要的实际类型的数组。转型中产生的警告必须用@Suppress Warnings来压制住,一旦获得了实际类型,就可以返回它,并获得想要的结果,如下:
public class GenericArrayWithTypeToken<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenericArrayWithTypeToken(Class<T> type, int sz) {
array = (T[])Array.newInstance(type, sz);
}
public void put( int index, T item) {
array[index] = item;
}
public T get( int index) { return array[index]; }
// Expose the underlying representation:
public T[] rep() { return array; }
public static void main(String[] args) {
GenericArrayWithTypeToken<Integer> gai =
new GenericArrayWithTypeToken<Integer>(
Integer. class , 10);
// This now works:
Integer[] ia = gai.rep();
}
}
Number[] numbers = new Integer[3];
numbers[0] = new Integer(0);
numbers[1] = new Integer(1);
numbers[2] = new Integer(2);
numbers[1] = new Double(3.4); //能通过编译,但运行时会报ArrayStoreException
***边界
可以用于在泛型的参数类型上设置限制条件,以强制规定泛型可以应用的类型,更重要的效果是你可以按照自己的边界类型来调用方法。因为擦除移除了类型信息,所以,可以用无界泛型参数调用的方法只是那些可以用Object调用的方法,但如果能将这个参数限制为某个类型子集,就可以用这些类型子集来调用方法。java泛型因此重用了extends关键字。
***通配符
class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
List<Fruit> fruit= new ArrayList<Apple>();//报错,不能把一个涉及Apple的泛型赋给一个涉及Fruit的泛型
List<? extends Fruit> fruit= new ArrayList<Apple>();
用通配符后,List<? extends Fruit>代表具有任何从Fruit机场的类型的列表。用这个List只能调用Fruit的方法。
***无界通配符
<?> 声明:想要Java的泛型来编写这段代码。并非是使用原生类型,但在此处,泛型参数可以持有任何类型。
当处理多个参数时,又是允许一个参数可以是任何类型,同时为其他参数确定某种特定类型。
使用确切类型替代通配符类型的好处是:可以用泛型参数来做更多的事,但使用通配符使得必须接受范围更宽的参数化类型作为参数。
***问题
1 任何基本类型都不能作为类型参数
可使用基本类型的包装器类和自动包装机制(自动包装机制不能应用于数组)。当考虑性能时,可使用专门适配基本类型的容器版本。