JavaSE第五十五讲:泛型高阶晋级

来源:互联网 发布:js比较两个数组的差异 编辑:程序博客网 时间:2024/05/01 15:50

1. 定义泛型类别

如果使用泛型,只要代码在编译时没有出现警告,就不会遇到运行时ClassCastException。

2. 限制泛型可用类型

在定义泛型类别时,预设可以使用任何的类型来实例化泛型类型中的类型(就比如前几讲中GenericFoo.java这个例子:里面的GenericFoo<T> ,这个T可以传任何类别的类型,String,Integer等),但是如果想要限制使用泛型类别时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口。

package com.ahuier.jdk5;import java.util.ArrayList;import java.util.HashMap;import java.util.LinkedList;import java.util.List;/* * 本类泛型T是可以传递任意类型的 * 但是这边定义的泛型T,必须是只能实现了List这个接口的类的类型才能传,即ArrayList、LinkedList可以作为参数,但是HashMap就不能作为参数。 * ListGenericFoo<T extends List> 这边用到 extends关键字来指定这个类型必须是继承某个类,或者实现某个接口。 * 注意,这边不管后面是类还是结构,都是必须要用 extends 关键字的。这是泛型的语法结构 */public class ListGenericFoo<T extends List> {private T[] fooArray;public T[] getFooArray() {return fooArray;}public void setFooArray(T[] fooArray) {this.fooArray = fooArray;}public static void main(String[] args) {ListGenericFoo<ArrayList> foo1 = new ListGenericFoo<ArrayList>();ListGenericFoo<LinkedList> foo2 = new ListGenericFoo<LinkedList>();ArrayList[] arrayList = new ArrayList[10];foo1.setFooArray(arrayList);LinkedList[] linkedList = new LinkedList[20];foo2.setFooArray(linkedList);ListGenericFoo<HashMap> foo3 = new ListGenericFoo<HashMap>();}}

试着传一个不合法的类型,ListGenericFoo<HashMap> foo3 = new ListGenericFoo<HashMap>();则Eclipse会出现提示:

- Bound mismatch: The type HashMap is not a valid substitute for the bounded parameter <T extends List> of the type ListGenericFoo<T> [绑定不匹配,对于HashMap这个类型对<T extends List> of the type ListGenericFoo<T> ]

3. 限制泛型的可用性

当没有指定泛型继承的类型或接口时,默认使用T extends Object,所以默认情况下任何类型都可以作为参数传入。

4. 类型通配声明

public class GenericFoo<T>{ 
private T foo; 
public void setFoo(T foo){ 
this.foo = foo; 

public T getFoo(){ 
return foo; 
}
}

GenericFoo<Integer> foo1 = null; 
GenericFoo<Boolean> foo2 = null;
那么 foo1 就只接受GenericFoo<Integer>的实例,而foo2只接受GenericFoo<Boolean>的实例。

現在您有這麼一個需求,您希望有一個參考名称foo可以接受所有下面的实例
foo = new GenericFoo<ArrayList>(); foo = new GenericFoo<LinkedList>();
简单的说,实例化类型持有者时,它必須是实现List的类别或其子类别,要定义这样一个名称,您可以使用 ‘?’ 通配字元,并使用“extends”关键字限定类型持有者的型态

package com.ahuier.jdk5;import java.util.ArrayList;import java.util.LinkedList;import java.util.List;public class GenericTest<T> {private T foo;public T getFoo() {return foo;}public void setFoo(T foo) {this.foo = foo;}public static void main(String[] args) {/* * ge 是一个引用,指向为空 * ge 的类型是一个GenricTest,并且里面的泛型是一个可以实现List接口的类型。 * ge 可以指向ArrayList 和   LinkedList; */GenericTest<? extends List> ge = null;ge = new GenericTest<ArrayList>();ge = new GenericTest<LinkedList>();}}
【说明】:这个程序与上面的ListGenericFoo.java进行比较,有什么不同:

    ListGenericFoo这个程序的泛型在声明的时候已经确定了泛型传递必须是实现List接口的类的类型,而GenericTest这个程序在定义的时候是默认传递Object的类型,也就是可以传递任何类型的泛型,但是在声明一个引用的时候,这个引用必须指向的是可以实现List接口的类型。这个程序是不同的概念。要理解清楚。

这边与之相对应的还有一个指定泛型类别位于所定义的类型的继承层次的上面用super关键字定义,由于用的很少,所以不加强调

/* * 这边与之相对应的还有一个指定泛型类别位于所定义的类型的继承层次的上面用super关键字定义 * 由于用的很少,所以不加强调 */GenericTest<? super List> ge2 = null;ge2 = new GenericTest<Object>();

package com.ahuier.jdk5;import java.util.ArrayList;import java.util.LinkedList;import java.util.List;public class GenericTest<T> {private T foo;public T getFoo() {return foo;}public void setFoo(T foo) {this.foo = foo;}public static void main(String[] args) {/* * ge 是一个引用,指向为空 * ge 的类型是一个GenricTest,并且里面的泛型是一个可以实现List接口的类型。 * ge 可以指向ArrayList 和   LinkedList; */GenericTest<? extends List> ge = null;ge = new GenericTest<ArrayList>();ge = new GenericTest<LinkedList>();/* * 这边与之相对应的还有一个指定泛型类别位于所定义的类型的继承层次的上面用super关键字定义 * 由于用的很少,所以不加强调 */GenericTest<? super List> ge2 = null;ge2 = new GenericTest<Object>();GenericTest<String> ge3 = new GenericTest<String>();ge3.setFoo("Hello world");GenericTest<? extends Object> ge4 = ge3; //String 类型是Object子类,所以可以直接赋值System.out.println(ge4.getFoo());/* * 代码段 1 */ge4.setFoo(null);System.out.println(ge4.getFoo());  /* * 代码段2 * 为什么代码段1 set Null则通过,而set “welcome”就通不过呢? *///ge4.setFoo("welcome");}}
【说明】:比较代码段1 和 代码段2 ,分析其原因:

    使用<?>或是<? extends SomeClass>的声明方式,意味著您只能通过该名称來取得所参考实例的信息,或者是移除[移除就是设置为null]某些信息,但不能增加它的信息,因为只知道当中放置的是SomeClass的子类,但不确定是什么类的实例,编译器不让您加入信息,理由是,如果可以加入信息的話,那么您就得記得取回的实例是什么类型,然后转换为原來的类型方可进行操作,这就失去了使用泛型的意义。


如果一个extends是Object的话,因为默认都是继承Object,所以以下两条语句是等价的:

语句1: GenericTest<? extends Object> ge4 = ge3;

语句2: GenericTest<?> ge4 = ge3;

通常我们都是用语句形式的来写的,这边要强调一下在JDK中很多都有使用这种方式的语句。