天天看點

泛型

二、泛型

Java5開始提供的新特性,表示不确定的類型。

注意:泛型是提供的javac編譯器使用的,它用于限定集合的輸入類型,讓

編譯器在源代碼級别上,擋住向集合中插入的非法資料。但編譯器編譯完之後,生産

的.class位元組碼檔案中不再代用泛型的資訊,依此不影響程式的運作效率,這個

過程被稱為“擦除”。

另外泛型還被用在方法或類上。

-------------------------------------

public class Demo2 {

public static List method1(){

//.....

return new ArrayList();

}

public static void method2(List list){

//...

public static void main(String[] args) {

List list1 = new ArrayList();//正确的

List<String> list2=new ArrayList<String>();//正确的

List<String> list3 = new ArrayList();//正确的

List<String> list3x = method1();//相容目前情況

List list4 = new ArrayList<String>();//正确的

List<String> list4x = new ArrayList<String>();

method2(list4x);//相容目前情況。

List<Object> list5 = new ArrayList<String>();//錯誤的

List<String> list6 = new ArrayList<Object>();//錯誤的

}

---------------------------

總結:可以兩邊都沒有;可以一邊有一邊沒有;

也可以兩邊都用,一旦兩邊都有的話,兩邊必須保持相同(不考慮邊界)。

2.2自定義泛型

分兩類:方法上的泛型和類上的泛型。

2.2.1方法上的泛型。

-------------------------

class Person{}

class Dog{}

public class God {

public Object kill(Object obj){

System.out.println("神弄死了:"+obj);

return obj;

public Person kill(Person p){

System.out.println("神弄死了的人是:"+p);

return p;

public Dog kill(Dog d){

System.out.println("神弄死了的狗是:"+d);

return d;

God god = new God();

Person p = god.kill(new Person());

Dog d = god.kill(new Dog());

------------------------------

神可以kill各種類型的對象,我們不可能為每一個類型都編寫一個方法,是以

需要使用定義在方法上的泛型。

注意:

(1).泛型需要先定義,再使用,方法上泛型需要定義在方法傳回值的前面,通常使用

一個大寫的英文字母(sun公司推薦使用T),也可以是任意的字母,但是不要使用

java中的關鍵字和常用的類(String)。

(2).定義在方法上的泛型的作用範圍是目前方法的内部。

-----------------

public <T> T kill(T t){

System.out.println("上帝弄死了"+t);

return t;

-------------------------

public <T> T save(T t){

System.out.println("上帝救活了:"+t);

如果把save方法上的<T>去掉,發現報錯,再次證明定義在方法

上的泛型隻用在目前方法的内部有效。

兩個方法上"T"不是同一個"T"。

-----------------------

God god = new God();

Person p = god.kill(new Person());

Dog d = god.save(new Dog());

如果想讓兩個方法上的T表示同一個,怎麼辦?

2.2.2.類上的泛型

定義在類上的泛型作用範圍是目前類的内部;

需要先定義再使用,類上的泛型定義在類名的後面。通常使用大寫的英文字母。

建立具有泛型的類(God)的對象時,通常需要指定泛型的具體類型。

如果在建立對象時,不明确指定泛型的具體類型,則預設為泛型的“上邊界”。

在類上定義的泛型不能用在靜态方法上,如果想在靜态方法上使用泛型,需要當該靜态

方法上重新定義。

另外檢視List接口源碼發現,它其實就是使用了定義在類上的泛型

public interface List<E> extends Collection<E>{

boolean add(E e);

void add(int index, E element);

E set(int index, E element);

<T> T[] toArray(T[] a);

}

-----------------------

想讓所有的神的對象之間公用泛型T:

public static <T> T sleep(T t){

System.out.println("神催眠了:"+t);

注意:在sleep方法上的T,和該方法所在類God<T>上的T不是同一個。

補充:在泛型使用中可以同時定義多個。

public class HashMap<K,V> extends AbstractMap<K,V>

implements Map<K,V>, Cloneable, Serializable

{

public V put(K key, V value){...}

...

}

神馬是“上邊界”?

一、泛型進階

!!通配符 ?

因為泛型沒有繼承的概念,是以當需要用一個“泛型引用”引用不同的泛型實作時,泛型寫

他們的共同父類是不行的,這時該怎麼做?引入一個新的概念,叫做泛型的通配符“?”,注意

通配符隻能用在聲明處,不能用在實作執行個體的過程中。

void print(Collection<?> c){//Collection of unknown

for(Object obj:c){

System.out.pritnln(obj);

}

new ArrayList<Stdudent>();

new ArrayList<Person>();

注意:由于參數c類型為Collection<?>,表示一種不确定的類型,是以在方法内部

不能調用與類型相關的方法。

如果沒有指定泛型預設類型,可以接收任意類型,有時希望進一步限制範圍,需要使用泛型的邊界:

!!上邊界:限定通配符的上邊界

extends-用來指定泛型的上邊界,和泛型通配符配合使用(List<? extends Person> list),指定具體的泛型實作必須是指定類或它子孫類。

List<? extends Number> list1 = new Vector<Number>();//正确的

List<? extends Number> list2 = new Vector<Integer>();//正确的

List<? extends Number> list3 = new Vector<String>();//錯誤的

List<? extends Person> list1 = null;

list1 = new Vector<Teacher>();

Teacher thr = new Teahcer();

list1.add(thr);

SomeOne<T extends Person>

示範和相關總結見:cn.tedu.v1.Demo2

!下邊界:限定通配符的下邊界

super:用來指定泛型的下邊界,和通配符一起使用。指定具體的泛型實作必須是指定類或指定的“超類”

? super Person

這個原因也是很簡單的, 因為我們所傳入的類都是Integer的類或其父類, 所傳入的資料類型可能是Integer到Object之間的任何類型, 這是無法預料的, 也就無法接收. 唯一能确定的就是Object, 因為所有類型都是其子類型.

使用? super E還有個常見的場景就是TreeSet有這麼一個構造方法:

TreeSet(Comparator<? super E> comparator)

就是使用Comparator來建立TreeSet, 那麼請看下面的代碼:

public class Person {

private String name;

private int age;

public Person(){}

public Person(String name,int age){

this.name = name;

this.age = age;

}//省略getters和setters

public class Student extends Person {

public Student() {}

public Student(String name,int age) {

super(name,age);

class comparatorTest implements Comparator<Person>{

@Override

public int compare(Person p1, Person p2) {

int num = p1.getAge() - p2.getAge();

return num == 0 ? p1.getName().compareTo(p2.getName()):num;

}

public class Demo {

public static void main(String[] args) {

TreeSet<? super Person> ts1 = new TreeSet<Person>(new comparatorTest());

ts1.add(new Person("Tom", 20));

ts1.add(new Person("Jack", 25));

ts1.add(new Person("John", 22));

System.out.println(ts1);

TreeSet<? super Student> ts2 = new TreeSet<Student>(new comparatorTest());

ts2.add(new Student("Susan", 23));

ts2.add(new Student("Rose", 27));

ts2.add(new Student("Jane", 19));

System.out.println(ts2);

}

三、總結:

"in out"原則, 總的來說就是:

in就是你要讀取出資料以供随後使用(想象一下List的get), 這時使用extends關鍵字, 固

定上邊界的通配符. 你可以将該對象當做一個隻讀對象;

out就是你要将已有的資料寫入對象(想象一下List的add), 這時使用super關鍵字, 固定下

邊界的通配符. 你可以将該對象當做一個隻能寫入的對象;

當你希望in的資料能夠使用Object類中的方法通路時, 使用無邊界通配符;List<?>

當你需要一個既能讀又能寫的對象時, 就不要使用通配符了.