1. 程式人生 > 其它 >java泛型機制

java泛型機制

  • 泛型概述

  • 泛型使用的必要性

  • 泛型類

  • 泛型介面

  • 泛型物件引用傳遞的解決方案

  • 泛型方法

  • 泛型的簡單應用
    ---本文中將介紹泛型的基礎知識以及簡單應用,後面還計劃學泛型的擦除機制以及在集合和反射機制中的應用

泛型的概述

泛型,字面上理解就是廣泛的資料型別。其實就是將資料型別進行引數化,可以達到進一步的程式碼複用和使得程式更安全的目的

  • 泛型的基本定義
    泛型的特點:在宣告一個變數或者屬性的時候不設定其資料型別,而是在指定結構使用的時候進行動態的配置。使用泛型解決了強制型別轉化的設計缺陷

泛型的必要性

在生活中資訊的傳送是並不可少的,但資訊表達的方式也是不盡相同的。怎樣實現程式碼複用得接收不同型別的訊息顯得很重要

不使用泛型的設計

//用於接收不同型別的訊息
class Massage{
	private Object content;//用Object接收所以型別的訊息
	public Massage(Object content){
		setContent(content);
	}
	public Object getContent(){
		return content;
	}
	public void setContent(Object content){
		this.content=content;
	}
	public void sentMassage(){
		System.out.println(content);
	}
}


public class MassageTest{
	public static void main(String[]args){
		Massage ma=new Massage("你今天有什麼計劃嗎?");
		Massage me=new Massage(98.3);
		ma.sentMassage();
		me.sentMassage();
		//String str=(String)me.getContent();//此處ClassCastException(失誤操作)
		//str.split();
		
		
	}
}
output:
你今天有什麼計劃嗎?
98.3
//Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
        at MassageTest.main(MassageTest.java:25)

該程式用Object類作為所以類的父類進行接收所以的型別的資料,可以實現相應的功能,沒有一點問題

但一般我們得到相應的資料之後,一般會做相應的一些處理,如程式中的註釋部分,其中一般會涉及向下轉型(用於呼叫子類特有的方法)向下轉型如果我們操作失誤,將可能會發生ClassCastException異常更重要的是這種異常為執行時異常,在編譯期間不會暴露,而在我們的執行期間才會暴露,這給我們的程式帶來了極大的安全隱患

而泛型的使用可以從根源上解決這個問題

為了解決這個問題,我們可以將這個Massage類設計成泛型類 由此我們引入泛型類

泛型類

泛型類在進行物件的例項化的時候,指定該泛型類所攜帶的泛型的具體型別


格式:
類名稱<具體類> 物件名稱= new 類名稱<具體類>();
在jdk1.7時對該格式進行了簡化:
類名稱<具體類> 物件名稱= new 類名稱<>();(好像也稱為鑽石表示式)

泛型類實現後面同樣的程式碼

//用於接收不同型別的訊息
class Massage<T>{//標識為一個泛型類,T該泛型的型別
	private T content;//此處型別由外部指定
	public Massage(T content){//由外部指定
		setContent(content);
	}
	public T getContent(){//由外部指定
		return content;
	}
	public void setContent(T content){//由外部指定
		this.content=content;
	}
	public void sentMassage(){
		System.out.println(content);
	}
}


public class MassageTest{
	public static void main(String[]args){
		Massage<String>ma=new Massage<String>("你今天有什麼計劃嗎?");
		Massage<Double>me=new Massage<Double>(98.3);
		ma.sentMassage();
		me.sentMassage();
	    String str=me.getContent();//此處失誤
		str.split("");
		
		
	}
}
編譯後:
MassageTest.java:25: 錯誤: 不相容的型別: Double無法轉換為String
            String str=me.getContent();//此處失誤
                                    ^

相信聰明的你已經發現不同了,用泛型類進行實現相同的功能,和普通類實現相比更安全,都是相同的錯誤,但泛型實現在編譯期間就可以泛型,而普通類進行實現需要在執行期間才能夠發現(如果開發要求比較嚴格,這種有警告資訊的程式碼一般不允許釋出)

注意:

  • 泛型的型別只能用類,基本資料型別需要使用對應的包裝類,有自動拆箱和裝箱的加持,也並不複雜
  • 當泛型指定的型別和傳入的型別不一致時,程式將會在編譯期間報錯
  • 當在例項化物件的時候沒有指定泛型的型別 即Massage ma=new Massage()時,為了保證程式不出錯,java將會把泛型型別預設為Object,但在編譯期間將會出現安全警告,但不影響執行
//用於接收不同型別的訊息
class Massage<T>{//標識為一個泛型類,T該泛型的型別
	private T content;//此處型別由外部指定
	public Massage(T content){//由外部指定
		setContent(content);
	}
	public T getContent(){//由外部指定
		return content;
	}
	public void setContent(T content){//由外部指定
		this.content=content;
	}
	public void sentMassage(){
		System.out.println(content);
	}
}


public class MassageTest{
	public static void main(String[]args){
		Massage ma=new Massage("你今天有什麼計劃嗎?");//沒有指定泛型型別
		Massage me=new Massage (98.3);//沒有指定泛型型別
		ma.sentMassage();
		me.sentMassage();
	    
		
		
	}
}
編譯時:
C:\Users\SWT\Desktop\java語言疑問測試程式碼>javac MassageTest.java
注: MassageTest.java使用了未經檢查或不安全的操作。
注: 有關詳細資訊, 請使用 -Xlint:unchecked 重新編譯。

output:
你今天有什麼計劃嗎?
98.3

多元泛型

上面的程式我們都只運用了一個泛型型別,其實我們可以設定多元泛型
舉例說明:

//驗證多元泛型的使用
class Tools<K,V>{
	
	private K key;
	private V value;
	public Tools(K key,V value){
		setKey(key);
		setValue(value);
	}
	public K getKey(){
		return this.key;
	}
	
	public void setKey(K key){
		this.key=key;
	}
	public V getValue(){
		return this.value;
	}
	public void setValue(V value){
		this.value=value;
	}
}

public class ToolsTest{
	public static void main(String[]args){
		Tools<String,Double>to=new Tools<String,Double>("湯姆貓",34.5);
        System.out.println("名稱:"+to.getKey());
        System.out.println("價值:"+to.getValue());		
	}
}
output:
名稱:湯姆貓
價值:34.5

泛型介面

必知:實現的介面的泛型型別需要和其子類的泛型型別相同

我們不僅可以定義泛型類,還可以定義泛型介面,泛型介面有2種實現方法

  • 方式一:介面的泛型型別和泛型類一起在例項化時指定
//泛型介面的實現
interface Info<T>{//該步和泛型類意義相同
public T getVar();
	
}

class Infolmpl<T> implements Info<T>{
	private T var;
	public Infolmpl(T var){
		this.setVar(var);
	}
	public void setVar(T var){
		this.var=var;
		
	}
	public T getVar(){
		return this.var;
	}
}


public class FInterfaceTest{
	public static void main(String[]args){
		Info<String> in=new Infolmpl<String>("天才在左,瘋子在右");//用子類例項化介面
		System.out.println(in.getVar());
		
		
	}
}
output:
天才在左,瘋子在右
  • 方式二:在介面中指定具體的泛型型別(這種方式可能會更常用)
//泛型介面的實現
interface Info<T>{//該步和泛型類意義相同
public T getVar();
	
}

class Infolmpl<T> implements Info<String>{//該處直接指定泛型型別
//指定泛型型別後,後面的定義就用String直接表示就好了
	private String var;
	public Infolmpl(String var){
		this.setVar(var);
	}
	public void setVar(String var){
		this.var=var;
		
	}
	public String getVar(){
		return this.var;
	}
}


public class FInterfaceTest{
	public static void main(String[]args){
		Info<String> in=new Infolmpl<String>("天才在左,瘋子在右");//用子類例項化介面
		System.out.println(in.getVar());
		
		
	}
}
output:
天才在左,瘋子在右

泛型物件引用傳遞的解決方案

//驗證泛型物件的引用傳遞
class Massages<T>{
	private T content;
	public Massages(T content){
		this.setContent(content);
	}
	public T getContent(){
		return this.content;
	}
	public void setContent(T content){
		this.content=content;
	}
	public void sentMassages(){
		System.out.println(this.content);
	}
	
}
public class MassagesTest{
	public static void info(Massages<String> ma){//進行引用傳遞
		ma.sentMassages();
	}
	public static void main(String[]ages){
		Massages<String> me=new Massages<>("同學你又在摸魚嗎?");
		info(me);
	}
}
output:
同學你又在摸魚嗎?

此時可以發現可以成功傳遞了

但如果我們換成下面一句程式碼,發現傳不過來了

Massages<Integer> me=new Massages<>(12);

MassagesTest.java:24: 錯誤: 不相容的型別: Massages無法轉換為Massages

可以得出一個我自以為是的推論:

編譯器將同一個類的不同泛型的物件的資料型別看成了不同的資料型別
就是Massages和Massages它將會認為了不同的資料型別

下面是驗證與推導的過程

為了解決此時泛型物件的引用傳遞我們想到了以下的3種解決方案

1.我們根據傳入泛型的型別,設計多種傳參函式進行過載,完成對該類的不同泛型物件的引用傳遞
2.我們知道Object類是所有類的父類,如果我們將傳參函式的泛型設定成Object,就可以接收不同的物件了
3.Massage和Massages編譯器認為其型別不同,但本質上將都同屬於Massage類,所以可以直接用Massages類進行接收,不進行泛型標識
方案一:

//驗證泛型物件的引用傳遞
class Massages<T>{
	private T content;
	public Massages(T content){
		this.setContent(content);
	}
	public T getContent(){
		return this.content;
	}
	public void setContent(T content){
		this.content=content;
	}
	public void sentMassages(){
		System.out.println(this.content);
	}
	
}
public class MassagesTest{
	//將傳參函式進行過載
	public static void info(Massages<String> ma){
		ma.sentMassages();
	}
	public static void info(Massage<Integer>ma){
		ma.sentMassages();
	}
	public static void main(String[]ages){
		Massages<Integer> me=new Massages<>(12);
		Massages<String> ma=new Massages<>("條條大路通羅馬");
		info(me);
		info(ma);
	}
}
MassagesTest.java:29: 錯誤: 對於info(Massages<Integer>), 找不到合適的 方法
                info(me);
                ^
    方法 MassagesTest.info(Massages<String>)不適用
      (引數不匹配; Massages<Integer>無法轉換為Massages<String>)
    方法 MassagesTest.info(Massage<Integer>)不適用
      (引數不匹配; Massages<Integer>無法轉換為Massage<Integer>)
2 個錯誤

但好像失敗了,編譯器不認為這樣的2個函式是過載

  • 方案二
//驗證泛型物件的引用傳遞
class Massages<T>{
	private T content;
	public Massages(T content){
		this.setContent(content);
	}
	public T getContent(){
		return this.content;
	}
	public void setContent(T content){
		this.content=content;
	}
	public void sentMassages(){
		System.out.println(this.content);
	}
	
}
public class MassagesTest{
	//將傳參函式進行過載
	public static void info(Massages<Object> ma){
	
		ma.sentMassages();
	}
	
	public static void main(String[]ages){
		Massages<Integer> me=new Massages<>(12);
		Massages<String> ma=new Massages<>("條條大路通羅馬");
		
		info(me);
		info(ma);
		
	}
}
C:\Users\SWT\Desktop\java語言疑問測試程式碼>javac MassagesTest.java
MassagesTest.java:29: 錯誤: 不相容的型別: Massages<Integer>無法轉換為Massages<Object>
                info(me);
                     ^
MassagesTest.java:30: 錯誤: 不相容的型別: Massages<String>無法轉換為Massages<Object>
                info(ma);

不出所料,Massages