天天看點

01 21Java中級之泛型1 泛型問題引出2 泛型基本定義3 泛型通配符4 泛型接口5 泛型方法

文章目錄

  • 1 泛型問題引出
  • 2 泛型基本定義
  • 3 泛型通配符
  • 4 泛型接口
  • 5 泛型方法

1 泛型問題引出

泛型從JDK 1.5之後追加到Java語言裡面的,其主要目的是為了解決ClassCastException的問題,在進行對象的向下轉型時都可能存在有安全隐患,而Java希望通過泛型可以慢慢解決掉此類問題。

//此代碼易出現ClassCastException
class Pointer{
	private Object x;
	private Object y;

	public Pointer(){}

	public Pointer(Object x, Object y){
		this.x = x;
		this.y = y;
	}

	public void setX(Object x){
		this.x = x;
	}

	public void setY(Object y){
		this.y = y;
	}

	public Object getX(){
		return this.x;
	}

	public Object getY(){
		return this.y;
	}
}

public class JavaDemo{

	public static void main(String[] args){
		Pointer point = new Pointer();

		point.setX(10);
		point.setY("北緯10度");

		int x = (Integer) point.getX();
		int y = (Integer) point.getY();

		System.out.println(x + "  " + y);
	}
}

           

2 泛型基本定義

如果要想避免項目之中出現ClassCastException最好的做法是可以回避掉對象的強制轉換,是以在JDK 1.5之後提供有泛型技術,泛型的本質在于類中的屬性或方法的參數與傳回值的類型可以由對象執行個體化的時候動态決定。

那麼此時就需要在類定義的時候明确的定義占位符(泛型标記)。

class Pointer<T>{
	private T x;
	private T y;

	public Pointer(){}

	public Pointer(T x, T y){
		this.x = x;
		this.y = y;
	}

	public void setX(T x){
		this.x = x;
	}

	public void setY(T y){
		this.y = y;
	}

	public T getX(){
		return this.x;
	}

	public T getY(){
		return this.y;
	}
}

public class JavaDemo{

	public static void main(String[] args){
		Pointer<Integer> point = new Pointer<Integer>();

		point.setX(10);
		point.setY("北緯10度");

		int x = (Integer) point.getX();
		int y = (Integer) point.getY();

		System.out.println(x + "  " + y);
	}
}

           

提示:關于預設的泛型類型

(1)由于泛型是屬于JDK 1.5之後的産物,但是在這之前已經有不少内置的程式類或者是接口廣泛的應用在項目開發之中,于是為了保證這些類或接口追加了泛型之後,原始的程式類依然可以使用,是以如果不設定泛型類型時,自動将使用Object作為類型,以保證程式的正常執行,但在編譯的時候出現警告資訊。

泛型定義完成後可以在執行個體化對象的時候進行泛型類型的設定,一旦設定之後,裡面的x與y的資料類型就與目前對象直接綁定了。

現在的程式代碼之中,由于Pointer類裡面設定的泛型類型為Integer,這樣所有的對應此泛型的屬性、變量、傳回值都将全部替換為Integer(隻局限于此對象之中)。這樣在進行處理的時候如果發現程式有錯誤,會自動地進行錯誤提示,同時也避免了對象的向下轉型處理(避免了安全隐患)。

泛型的使用注意點:

(1)泛型之中隻允許設定引用類型,如果現在要操作基本類型必須使用包裝類;

(2)從JDK 1.7開始,泛型對象執行個體化可以簡化為Pointer point = new Pointer<>();

使用泛型可以解決大部分的類對象的強制轉換處理,這樣的程式才是一個合理的設計。

3 泛型通配符

雖然泛型幫助開發者解決了一系列的對象的強制轉換所帶來的安全隐患,但是從另外一個角度來講,泛型也帶來一些新的問題。

問題:引用傳遞問題

class Message<T>{
	public T content;

	public Message(){}

	public Message(T content){
		this.content = content;
	}

	public T getContent(){
		return this.content;
	}

	public void setContent(T content){
		this.content = content;
	}
}

public class JavaDemo{

	public static void main(String[] args){
		Message<String> msgA = new Message<>();
		msgA.setContent("hello");
		
	}

	public static void fun(Message<String> temp){
		System.out.println(temp.getContent());
	}
}
           

此時最大的缺點,問題的關鍵在于fun()方法上,如果真的去使用泛型不可能隻是一種類型,也就是說,fun方法可以接收任意種類型的資料。可能第一時間我們會想到重載,但是重載會存在一些參數類型問題,代碼如下:

class Message<T>{
	public T content;

	public Message(){}

	public Message(T content){
		this.content = content;
	}

	public T getContent(){
		return this.content;
	}

	public void setContent(T content){
		this.content = content;
	}
}

public class JavaDemo{

	public static void main(String[] args){
		Message<String> msgA = new Message<>();
		msgA.setContent("hello");
		
	}

	public static void fun(Message<String> temp){
		System.out.println(temp.getContent());
	}

	public static void fun(Message<Integer> temp){
		System.out.println(temp.getContent());
	}
}
/*
JavaDemo.java:31: 錯誤: 名稱沖突: fun(Message<Integer>)和fun(Message<String>)具有相同疑符
        public static void fun(Message<Integer> temp){
                           ^
1 個錯誤
*/
           

通過上面代碼我們可以發現重載根本行不通,那麼我們現在可能會考慮用隻用Message,這樣寫的确沒問題,但是會存在安全隐患,就是在函數中我們可以修改傳入的資料。

public class JavaDemo{

	public static void main(String[] args){
		Message<String> msgA = new Message<>();
		msgA.setContent("hello");
		fun(msgA);
		Message<Integer> msgB = new Message<>();
		msgB.setContent(12);
		fun(msgB);
	}

	public static void fun(Message temp){
		temp.setContent(100);
		System.out.println(temp.getContent());
	}
}

           

那麼我們究竟應該如何做呢?答案是使用泛型通配符?。

public class JavaDemo{

	public static void main(String[] args){
		Message<String> msgA = new Message<>();
		msgA.setContent("hello");
		fun(msgA);
		Message<Integer> msgB = new Message<>();
		msgB.setContent(12);
		fun(msgB);
	}

	public static void fun(Message<?> temp){
		System.out.println(temp.getContent());
	}
}

           

此時在fun()方法裡面由于采用了Message結合通配符的處理是以可以接受所有的類型。

在“?”這個通配符的基礎之上實際上還提供有兩個小的通配符:

(1)? extends 類:設定泛型的上限;

|——例如:定義“? extends Number”:表示該泛型類型隻允許設定Number或Number的子類;

(2)? super 類:設定泛型的下限;

|——例如:定義“? super String”:表示隻能使用String或其父類。

範例:觀察泛型上限配置

class Message<T extends Number>{
	public T content;

	public Message(){}

	public Message(T content){
		this.content = content;
	}

	public T getContent(){
		return this.content;
	}

	public void setContent(T content){
		this.content = content;
	}
}

public class JavaDemo{

	public static void main(String[] args){
		
		Message<Integer> msgB = new Message<>();
		msgB.setContent(12);
		fun(msgB);
	}

	public static void fun(Message<? extends Number> temp){
		System.out.println(temp.getContent());
	}
}

           

範例:設定泛型的下限

public class JavaDemo{

	public static void main(String[] args){
		
		Message<String> msgB = new Message<>();
		msgB.setContent("hello");
		fun(msgB);
	}

	public static void fun(Message<? super String> temp){
		System.out.println(temp.getContent());
	}
}
           

對于通配符而言是一個重要的概念,并且要求你一定可以了解此概念的定義,在日後學習Java一些系統類庫的時候會見到大量的通配符使用。

4 泛型接口

泛型除了可以在類上定義之外也可以直接在接口之中進行使用。定義代碼如下:

interface IMessage<T>{
	public abstract String echo(T t);
}
           

對于泛型接口的子類而言現在就有兩種實作方式。

實作方式一:在子類中繼續設定泛型定義

interface IMessage<T>{
	public abstract String echo(T t);
}

class Message<T> implements IMessage<T>{
	public String echo(T t){
		return "ECHO: " + t;
	}
}

public class JavaDemo{

	public static void main(String[] args){
		IMessage<String> msg = new Message<String>();
		System.out.println(msg.echo("Hello"));
	}
}

           

實作方式二:在子類實作父接口的時候直接定義出具體泛型類型

interface IMessage<T>{
	public abstract String echo(T t);
}

class Message implements IMessage<String>{
	public String echo(String t){
		return "ECHO: " + t;
	}
}

public class JavaDemo{

	public static void main(String[] args){
		IMessage<String> msg = new Message();
		System.out.println(msg.echo("Hello"));
	}
}

           

5 泛型方法

在泛型類之中如果将泛型标記寫在了方法上,那麼這樣的方法就被稱為泛型方法,但是泛型方法不一定要出現在泛型類之中,即如果一個類上沒有定義泛型,也可以使用泛型方法。

public class JavaDemo{

	public static void main(String[] args){
		Double[] n = fun(1.0, 2.0, 3.0);
		for(double temp : n){
			System.out.println(temp);
		}
	}

	public static <T> T[] fun(T ...t){
		return t;
	}
}

           

項目開發時這種泛型方法很常見。