1. 程式人生 > >黑馬程式設計師——Java集合框架(二)之泛型

黑馬程式設計師——Java集合框架(二)之泛型

培訓java培訓、java學習型技術部落格、期待與您交流!------------

              泛型

一、泛型概述

1.什麼是泛型?

泛型就是指將資料型別引數化,把以前固定的資料型別用一個代表資料型別的引數進行表示,該引數可以接受傳入的任意資料型別。可以這樣理解,如果把常量數值1當做一種資料型別,那麼將常量數值1變成變數a的過程就可以理解為引數化過程,此時a既可以接受常量數值1的資料型別,也可以接受常量數值2、3等的資料型別,那麼此時變數a就可以理解為泛型。

  泛型是jdk1.5版本出現的新特性,用於解決安全問題,是一個安全機制。

2.泛型的好處

a)將執行時期出現的問題ClassCastException,轉移到了編譯時期。方便於程式設計師解決問題。讓執行時期問題減少、安全。

  b)避免了強制轉換的麻煩。如在實現某一個介面時,指定傳入介面方法的實參的型別的話,在複寫該介面方法時就可以直接使用指定型別,而不需要強制轉換。

 3.泛型格式

  通過<>來定義要操作的引用資料型別。

  泛型:

   格式:<T>

   示例:ArrayList<T>,表示該集合存入的元素為T型別,T可以為任意物件型別。

  泛型例項:泛型例項就是將泛型引數例項化。

   格式:<具體資料型別>

   示例:ArrayList<String>,表示該集合存入的元素必須為String型別。

 4.萬用字元

  萬用字元也稱佔位符,表示格式為:<?>

  示例:ArrayList<?>,表示該集合存入的元素為任意物件型別,?表示佔位符,也稱萬用字元。

 5.泛型注意事項

  1)泛型是提供給javac編譯器使用,生成的位元組碼檔案會去掉“型別”資訊,使程式執行效率不受影響,對引數化的泛型型別,getClass()方法的返回值和原始型別完全一樣。

  2)由於編譯生成的位元組碼會去掉泛型的型別資訊,只要能跳過編譯器,就可以往某個泛型集合中加入其它型別的資料,如用反射得到集合,再呼叫add方法即可。

 6.問題思考

  1)在使用java提供的物件時,什麼時候寫泛型?

   答:泛型通常在集合框架中很常見,只要見到<>就要定義泛型。其實<>就是用來接收型別的。當使用集合時,將集合中要儲存的資料型別作為引數傳遞到<>中即可。

二、泛型類

1.什麼是泛型類

  當泛型定義在類名上時,該類就叫泛型類。

 2.泛型類的格式

  泛型類:

   class 類名<T> { 類的內容

   其中,T為泛型引數,類中內容可以存在使用T的方法或成員變數或返回值型別或引數型別等。

  泛型例項類:是指在類中定義泛型,並且泛型引數已經例項化,即已經確定資料型別。

   calss 類名<具體資料型別> { 類的內容 }

   例如:class 類名<String> { 類的內容 },表示將類中String型別提升為Object型別,在類中String物件不能使用其特有方法,且靜態方法中不能存在String物件。

  示例1:定義泛型引數類

class Utils<QQ>
{
	private QQ q;

	public void setObject(QQ q)
	{
		this.q=q;
	}

	public QQ getObject()
	{
		return q;
	}
}
  示例2:定義泛型例項類
class Person<Character>
{
	private int age;
	private String name;

	public void setName(String name)
	{
		this.name=name;
	}

	public void setAge(int age)
	{
		this.age=age;
	}
}

3.程式碼練習

/*
練習1:自定一個泛型類,完成對任意物件的操作。
*/
class  GenericTest1
{
	public static void main(String[] args) 
	{
		Utils<Worker> u=new Utils<Worker>();

		u.setObject(new Worker());
		Worker w=u.getObject();
	}
}

class Worker
{
}

//定義一個泛型類
class Utils<QQ>
{
	private QQ q;

	public void setObject(QQ q)
	{
		this.q=q;
	}

	public QQ getObject()
	{
		return q;
	}
}<strong>
</strong>

三、泛型方法

 1.概述

  在方法上定義泛型,該方法就稱為泛型方法。

 2.格式:

  修飾符 <T> 返回值型別 函式名稱(引數型別1 名稱1,...)

  例如:public <T> void show(T t){ 方法內容 }

 3.示例:

  在類中定義泛型方法:

class PrintDemo
{
	//定義泛型方法
	public <T> void print(T t)
	{
		System.out.println(t);
	}
}
四、靜態泛型方法

 1.概述

  在靜態方法上定義泛型,該靜態方法就稱為靜態泛型方法。

 2.格式:

  修飾符 static <T> 返回值型別 函式名稱(引數型別1 名稱1,...)

  例如:public static <T> void show(T t){ 方法內容 }

 3.示例:

  在類中定義靜態泛型方法:

class PrintDemo
{
	//定義靜態泛型方法
	public static <T> void print(T t)
	{
		System.out.println(t);
	}
}

五、泛型介面

 1.概述

  在介面上定義泛型,該介面就稱為泛型介面。

 2.格式:

  泛型介面:

   interface 介面名<T> { 介面的內容 }

  泛型例項介面:

   interface 介面名<具體資料型別> { 介面的內容 }

   示例:interface 介面名<String> { 介面的內容 },表示將該介面中的String型別提升為Object型別,String物件只能呼叫Object來中的方法,而不能呼叫其特有方法。

 3.程式碼練習:

<span style="font-size:14px;">/*
練習2:定義泛型介面,並利用泛型介面進行操作。
*/

class  GenericTest2
{
	public static void main(String[] args) 
	{
		/*
		InterImp i=new InterImp();
		i.show("hello");
		*/

		InterImp<Integer> i=new InterImp<Integer>();
		i.show(2);
	}
}

//定義一個泛型介面
interface Inter<T>
{
	void show(T t);
}

/*
class InterImp implements Inter<String>
{
	public void show(String s)
	{
		System.out.println("show run:"+s);
	}
}
*/

//定義一個類,實現泛型介面,併成為泛型引數類
class InterImp<T> implements Inter<T>
{
	public void show(T t)
	{
		System.out.println("show run:"+t);
	}
}</span>
  程式執行後的結果如下圖:


六、泛型限定

 1.概述

  泛型限定是指將泛型的引數型別限定範圍。

 2.格式

  1)限定上限:<? extends E>

   示例:ArrayList<? extends E>,表示該集合存入的元素只能為E型別或E的子型別。

  2)限定下限:<? super E>

   示例:ArrayList<? super E>,表示該集合存入的元素只能為E型別或E的父型別。

 3.程式碼練習:

  練習3:利用泛型上限操作不同物件資料型別的集合。

import java.util.*;
/*
練習3:利用泛型上限操作不同物件資料型別的集合。
*/
class GenericTest3
{
	public static void main(String[] args) 
	{
		ArrayList<Person> al=new ArrayList<Person>();

		al.add(new Person("abc01"));
		al.add(new Person("abc02"));
		al.add(new Person("abc03"));

		ArrayList<Student> all=new ArrayList<Student>();

		all.add(new Student("java01"));
		all.add(new Student("java02"));
		all.add(new Student("java03"));

		printArr(al);
		printArr(all);

	}

	public static void printArr(ArrayList<? extends Person> a)
	{
		Iterator<? extends Person> i=a.iterator();
		while (i.hasNext())
		{		
			System.out.println(i.next().getName());
		}
	}
}

class Person
{
	private String name;

	Person(String name)
	{
		this.name=name;
	}

	public String getName()
	{
		return name;
	}
}

class Student extends Person
{
	Student(String name)
	{
		super(name);
	}
}
   程式執行後的結果如下圖:


   練習4:利用泛型下限操作不同物件型別的集合。

import java.util.*;

class GenericTest4 
{
	public static void main(String[] args) 
	{
		TreeSet<Student> ts=new TreeSet<Student>(new Comp());

		ts.add(new Student("student05"));
		ts.add(new Student("student03"));
		ts.add(new Student("student02"));

		TreeSet<Worker> tss=new TreeSet<Worker>(new Comp());

		tss.add(new Worker("worker04"));
		tss.add(new Worker("worker03"));
		tss.add(new Worker("worker06"));

		for (Iterator i=ts.iterator();i.hasNext() ; )
		{
			Student s=(Student)i.next();
			System.out.println(s.getName());
		}

		for (Iterator i=tss.iterator();i.hasNext() ; )
		{
			Worker w=(Worker)i.next();
			System.out.println(w.getName());
		}
	}
}

//泛型引數指定Person,表示可以接受Person的子型別,即可以接收Worker和Student
class Comp implements Comparator<Person>
{
	public int compare(Person p1,Person p2)
	{
		return p1.getName().compareTo(p2.getName());
	}
}

class Person
{
	private String name;

	Person(String name)
	{
		this.name=name;
	}

	public String getName()
	{
		return name;
	}
}

class Student extends Person
{
	Student(String name)
	{
		super(name);
	}
}

class Worker extends Person
{
	Worker(String name)
	{
		super(name);
	}
}
  程式的執行結果如下圖:



七、總結

 1.類上定義泛型只有兩種情況,泛型類和泛型例項類,即為 class 類名<T> {}和class 類名<具體資料型別> {},且格式唯一。方法上定義泛型只有一種情況,就是在返回值型別前加泛型<T>。

 2.對於泛型引數類,類中的內容都可以引用該泛型引數,但不包括靜態方法,因為靜態方法先於物件存在。對於泛型例項類,如果某個資料型別被例項化,例如class 類名<String> { 類的內容 },則表示將類中String型別提升為Object型別,在類中String物件不能使用其特有方法,且靜態方法中不能存在String物件。對於泛型方法中的泛型引數只能在方法上或方法內中引用。如果類中出現了泛型引數,但在方法和類上都沒有定義泛型,則編譯不通過。

 3.泛型限定中的泛型引數必須在方法中或類中有定義,否則不通過。泛型例項和萬用字元只能出現在定義了泛型的類或介面名上。

 4.泛型型別的物件和萬用字元物件不能呼叫其特有方法,只能呼叫Object類中的方法。

 5.萬用字元?只能作為泛型的佔位符使用,表示不確定接受型別。不能作為引數資料型別進行引用。

 6.泛型的問題:為了方便說明問題,假定存在三個類,分別為Person、Teacher、Student,且存在Student繼承Teacher,而Teacher繼承Person的關係。

  1)<T super 具體資料型別>,如<T super Student>,是不允許出現這種格式的。  

   示例1:public <T superStudent> void print(Collection<T > coll){}:錯誤格式。

   示例2:public <T> voidprint(Collection<T super Student> coll){}:錯誤格式。

  2)<T extends 具體資料型別>,如<T extendsPerson>,只能出現在泛型方法定義上。

   示例1:public <T> voidprint(Collection<T extends Person> coll){}:<Textends Person>沒有出現在泛型方法定義中,而是在其他地方,所以格式錯誤。

   示例2:public <T extends Person>void print(Collection<T > coll){}:<T extends Person>出現在泛型方法定義上,所以格式正確。

  3)<? super T>和<? extends T>不能用來定義泛型類、泛型介面和泛型方法。

   示例1:public<? extends T> void print(Collection<T > coll){}:<? extends T>不能用來定義泛型方法,格式錯誤。

   示例2:public <T> voidprint(Collection<? extends T> coll){}:格式正確。

   示例3 : public <? super T> void print(Collection<T> coll){}: <? super T>不能用來定義泛型方法,格式錯誤。

   示例4:public <T> void print(Collection<?super T> coll){}:格式正確。

  4) <? super T>、<? extends T>和<T>必須依賴於泛型類或泛型介面或泛型方法而存在,應為泛型引數T必須要進行定義。

   在泛型方法中,下面三種寫法在泛型限定上是等效的,即接收的型別都為Person或Person的子類。

    格式1:public <T extends Person> voidprint(Collection<? super T> coll){} 

    格式2:public <T extends Person> voidprint(Collection<T> coll){}

    格式3:public <T extends Person> voidprint(Collection<? extends T> coll){}

   在泛型類或介面中,下面三種寫法在泛型限定上是不同的。

   例如:存在一個泛型類A<T>,包含三種格式,如下:    

     classA<T>

    {

    格式1:public void print(Collection<? super T> coll){} :當newA<Student>時,print方法只能接收泛型為Student或Student的父類的集合。

    格式2:public void print(Collection<T> coll){}:當newA<Student>時,print方法只能接收泛型為Student的集合。

    格式3:public void print(Collection<? extends T> coll){}:當new A<Student>時,print方法只能接收泛型為Student或Student的子類的集合。

    }

  5)<?super 具體資料型別>、<? extends 具體資料型別>和<具體資料型別>不需要依賴於泛型類或泛型介面或泛型方法而存在,因為三種都不存在泛型引數,因此不需要進行定義。  

6)型別不相容問題。

   情況1:ArrayList<String> al=new ArrayList<Object>();

   情況2:ArrayList<Object> al=new ArrayList<String>();

  7)編譯出現安全提醒。

   情況1:ArrayList<String> al=new ArrayList();

  注:ArrayList al=new ArrayList<String>();不出現安全提醒,因為泛型是jdk1.5版本出現的,老版本應該容納新版本。