Java 泛型方法/接口、泛型限定

 文章目錄
     一、爲什麼要定義泛型方法
     1、從泛型類到泛型方法的演變過程
     2、定義泛型方法的好處
     二、創建一個泛型方法格式
     常用的形式
     三、泛型接口
     1、格式
     2、例子
    四、類型通配符(泛型的高級應用)
     1、什麼是通配符 (?)
     2、類型通配符上限定(? extends T =>可以接收T類型或者T的子類型)
     3、類型通配符下限定 (? super T => 可以接收T類型或者T父類型)

一、爲什麼要定義泛型方法

1、從泛型類到泛型方法的演變過程
我們先來看個例子

//定義一個泛型類,並定義如下兩個方法
class Test<T>
{
	public  void show(T t){
		System.out.println(t);
	}
     public void print(T t){
    	System.out.println(t);
  }	
  /*  以前是這樣定義,現在一個方法搞定
  	public void show(String t){	
	}
	public void show(Integer t){	
	}
	或者
	public void show(Object obj){	
	}
  */
}

//在manin方法代碼如下
	public static void main(String[] args) {
	   
		Test<String> d = new Test<String>();
		d.show("java");
		d.print("Object-C");
		
		Test<Integer> e = new Test<Integer>();
		e.show(2);
		e.print(new Integer(5));	
	}

上面是一個簡單的代碼demo,運行沒問題,正常輸出。但是你會發現,其實代碼有點冗餘。我們定義了一個泛型類,並定義了 show(T t)print(T t)方法。發現: 泛型類定義的泛型,在整個類中有效如果被方法使用,那麼泛型類的對象明確要操作的具體類型後,所要操作的類型已經固定了。就像上面main方法中。對象d,只能操作String類型,如果你要操作其他類型,只能額外去創建其他泛型對象e

設想下,如果我能把泛型定義在方法上,這樣不就可以優雅解決問題。於是變化代碼如下

//定義一個類,並定義如下兩個泛型方法
class Test
{
	public <T>  void show(T t){
		System.out.println(t);
	}
	
    public <T> void print(T t){
    	System.out.println(t);
  }	
   public <U,T> void sum(U u,T t){
	   System.out.println(u+" version is "+t);
   }
}
// main方法如下
public static void main(String[] args) {
		Test d = new Test();
		d.show("java");
		d.print(5);
		d.sum("java", new Double(8));
	}

Test不再是泛型類,泛型方法show(T t)print(T t)sum(U u,T t) 更具有擴展性。

2、定義泛型方法的好處

泛型方法可以讓不同方法操作不同類型,且類型還不確定。
與泛型類不同,泛型方法的類型參數只能在它鎖修飾的泛型方法中使用。

二、創建一個泛型方法格式

1、常用的形式

訪問修飾符 [static][final] <類型參數列表> 返回值類型 方法名([形式參數列表])

備註:[] 代可有可無的意思,泛型方法可以是實例方法或靜態方法,類型參數可以在靜態方法中,這是與泛型類最大的區別

三、泛型接口

1、格式

interface 接口名<類型參數表>

2、例子

定義一個泛型接口

interface showInterface<T>
{
	public void show(T t);
	
}

(1) 實現類確定了類型

class ShowClass implements showInterface<String>{
	public void show(String t){
		System.out.println("show:"+t);
	}
}

(2) 實現類類型不確定

class ShowClass<T> implements showInterface<T>{
	public void show(T t){
		System.out.println("show:"+t);
	}
}

main方法

	public static void main(String[] args) {
	
		ShowClass<Integer> obj = new ShowClass<Integer>();
		obj.show(6);
        /*
		ShowClass obj = new ShowClass();
		obj.show("java");
	    */
	}

四、類型通配符(泛型的高級應用)

1、什麼是通配符 (?)
看個例子就明白了。定義兩個集合,分別輸出兩個集合的元素。

	public static void main(String[] args) {

		ArrayList<String> a1 = new ArrayList<String>();
		a1.add("a");
		a1.add("b");
		a1.add("c");
		
		ArrayList<Integer> a2 = new ArrayList<Integer>();
		a2.add(1);
		a2.add(2);
		a2.add(3);
	}

在我們沒學習泛型之前,我們會封裝靜態方法如下:

	public static void printList(ArrayList list){
	  for (Iterator iterator = list.iterator(); iterator.hasNext();) 
	  {
		Object object = (Object) iterator.next();
		System.out.println(object);	
	   }
	}

代碼買啥毛病,運行也正確。會有一個疑問。爲什麼參數沒定義泛型,但是卻可以接受泛型呢?泛型是jdk1.5出來的,老版本肯定要兼容新版本。

在我們學習泛型方法後,我們進一步將代碼修改如下:

	public static <T>void vistor(ArrayList<T> a){
		Iterator<T> iterator = a.iterator();
	    while(iterator.hasNext()){
	    	T t = iterator.next(); 
	    	System.out.println(t);
	    }
	}

定義一個泛型方法。如果是泛型類,是不允許泛型定義在static上面的

如果不想定義泛型方法,又能夠解決問題呢?這就要用到一個通配符的玩意

	//佔位符,也稱爲通配符。表示元素類型可以匹配任意類型
	public static void sop(ArrayList<?> a){
		for(Iterator<?> it = a.iterator();it.hasNext();){
			System.out.println(it.next());
		}
	}

泛型方法T如果是具體類型的話,可以接收T t = iterator.next();。?是佔位符,不明確具體類型,無法接收。

這種帶通配符ArrayList<?> aList 僅僅表示他是各種泛型的父類,並不能把元素添加到其中。

我做了奇怪的例子

 ArrayList<?> list = new ArrayList<String>();
list.add(null);

上面說,帶通配符ArrayList<?> aList 僅僅表示他是各種泛型的父類,並不能把元素添加到其中。是不是想違背了?仔細想想,null是任意引用類型的實例。這比較特殊。

2、類型通配符上限定(? extends T)

同樣看一個例子. 定義一個集合,遍歷元素的方法並輸出。

//定義一個Person類
class Person
{
	private String name;
	public Person(String name){
		this.name = name;
	}
	public String getNmae(){
		return this.name;
	}
}
// 定義一個Student 並繼承 Person
class Student extends Person
{
	Student(String name){
		super(name);
	}
}

main方法如下
public static void main(String[] args) {
		
		ArrayList<Person> a1 = new ArrayList<Person>();
		a1.add(new Person("abc1"));
		a1.add(new Person("abc2"));
		a1.add(new Person("abc3"));

		printMethod(a1);
		
		
		// 下面是錯誤的。a2存的是Person,存在繼承的話,也能放worker。但是等號右邊只能存Student,存不進worker.類型安全問題。左右兩邊要一致
		ArrayList<Student> a2 = new ArrayList<Student>();
		a2.add(new Student("abc--1"));
		a2.add(new Student("abc--2"));
		a2.add(new Student("abc--3"));
		printMethod(a2); // ArrayList<Person> a2 = new ArrayList<Student>();
		//如果我想調用也printMethod(a2);沒毛病。怎麼做?。 第一想法直接給佔位符。但是帶來一問題,不能調用具體方法。
	}
	
	// 與main方法同級的靜態工具類方法
	public static void printMethod(ArrayList< Person> a1){
		Iterator<Person> it = a1.iterator();
		while(it.hasNext()){
			System.out.println(it.next().getNmae());
		}
	}

如果我想調用也printMethod(a2);沒毛病。怎麼做?。 第一想法直接給佔位符。但是帶來一問題,不能調用具體方法。泛型也是一樣的。如果我們想兩者兼得,技能打印Stuednt,也能答應Person,我們將printMethod修改如下

	public static void printMethod(ArrayList<? extends Person> a1){
		Iterator<? extends Person> it = a1.iterator();
		while(it.hasNext()){
			System.out.println(it.next().getNmae());
		}
	}

我們稱? extends T 爲 泛型上限定: 可以接收T和T的子類

3、類型通配符下限定<? super T>

接收T類型或者T的父類型
在這裏插入圖片描述
從集合TreeSet的構造方法我們可以看到 下限定的運用。我們也可以集合實際例子。

TreeSet<Person> ts = new TreeSet<Person>();  //Comparator<? super E> comparator
ts.add(new Person("d"));
ts.add(new Person("f"));
ts.add(new Person("g"));	

比較器如下

// 這邊的T寫Student和Person都是可以的
class comp implements Comparator<Person>
{
	public int compare(Person s1,Person s2){
		return s1.getNmae().compareTo(s2.getNmae());
	}
}

其實限定的目的是爲了擴展指定類型。

通過上面比較器參數和我們實現的比較器。如果我們比較器的泛型爲 Student,那麼只能比較Student。當未來某一天,Person有新子類出現的話,那麼該比較器就不適用了。所以Java 的API 的考慮擴展性的同時,已經設置了泛型下限定,你可以傳T類型或者T的父類型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章