1. 程式人生 > >java 泛型使用詳解

java 泛型使用詳解

1、概述

泛型是Java SE 1.5的新特性,泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。在使用的時候,具體指定操作資料型別,這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。
在Java SE 1.5之前,沒有泛型的情況的下,通過對型別Object的引用來實現引數的“任意化”,“任意化”帶來的缺點是要做顯式的強制型別轉換,而這種轉換是要求開發者對實際引數型別可以預知的情況下進行的。對於強制型別轉換錯誤的情況,編譯器可能不提示錯誤,在執行的時候才出現異常,這是一個安全隱患。
泛型的好處是在編譯的時候檢查型別安全,並且所有的強制轉換都是自動和隱式的,以提高程式碼的重用率。

2、泛型舉例

例子1:

//泛型例子
public static void testGenerics(){

        ArrayList list = new ArrayList();
        list.add("nihao");
        list.add(100);

        for(int i =0;i<list.size();i++){
            String content = (String)list.get(i);
            System.out.println(content);
        }

    }

上述程式碼編譯可以正常通過,但執行會報一下錯誤:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
說明:
因為ArrayList內部儲存資料用的是Object陣列,所以可以向ArrayList新增任意型別資料(String,Integer),所以在編譯期間可以正常通過。但是再執行期間,由於添加了Integer資料,強制轉換成String使用的時候就會報錯。為了在編譯期間(編譯器做出錯誤提示)就可以解決此類問題,泛型便出現了。
我們將上述ArrayList 的宣告初始化程式碼修改如下,編譯器會在編譯階段就會發現類似這樣的問題。

這裡寫圖片描述
如上圖,可以發現,eclipse做出了錯誤提醒。
例子2:

public static void testRunGenerics(){
        List<String> list1 = new ArrayList<String>();
        List<Long> list2 = new ArrayList<Long>();

    System.out.println(list1.getClass().equals(list2.getClass()));

    }

執行結果:true
上述例子說明,java程式碼在編譯後,會去除泛型資訊。也即是說,java中泛型只是在編譯階段有效。在編譯過程中,正確檢驗泛型結果後,會將泛型相關資訊擦除,並在物件進入和離開方法的邊界處,新增型別檢查,和型別轉換的方法。
一句話:泛型型別,只在編譯階段提供,型別資訊,用於檢查型別安全,和生成型別轉換方法。編譯成的class檔案,其實都是相同的基本型別。

3、泛型的定義和使用

泛型有三種使用方式,泛型介面,泛型類,泛型方法;

泛型介面

Show.java檔案

/**
 * 
 */
package generics;

/**
 * @author chzhao
 *
 */
public interface Show <T,M>{

    public void show(T t,M m);

}

TestGeneric.java檔案

package generics;

import java.util.Date;


//在類的定義的時候指定第一個引數為String,另一個引數不指定任何型別
class ShowTest <M> implements Show<String,M>{

    @Override
    public void show(String t, M m) {
        // TODO Auto-generated method stub
        System.out.println(t);
        System.out.println(m);
    }

}

public class TestGeneric {

    public static void main(String[] args) {
        //可以在使用的時候指定第二個引數型別。
        //注意介面Show中指定引數,第一個引數必須和
        //ShowTest定義指定的第一個引數型別相同,如不同編譯器報錯。
        Show <String,Date>showTest = new ShowTest<Date>();
        Date date = new Date();
        showTest.show("nihao", date);


    }
}

執行結果:
nihao
Sat Feb 24 15:01:17 CST 2018

泛型類

在類的定義中使用泛型,我們稱這樣的類為泛型類。jdk中最典型的的泛型類,如List Set Map。
泛型類的基本寫法:

class 類名稱 <泛型標識:可以隨便寫任意標識號,標識指定的泛型的型別>{
  private 泛型標識 /*(成員變數型別)*/ var; 
  .....

  }
}

TestGeneric.java檔案

package generics;

import java.util.Date;


//在例項化泛型類時,必須指定V的具體型別
//此處V可以寫成任意符合java規則的標識
class GenericValue <V>{
     //value這個成員變數的型別為V,V的型別由外部指定  
    private V value;
    //泛型構造方法,形參v的型別由該類具體使用時指定。
    public GenericValue(V v){
        this.value = v;
    }
    //泛型方法 返回值由該類具體使用時指定。
    public V getValue(){

        return  value;
    }

}

public class TestGeneric {

    public static void main(String[] args) {

        GenericValue<String> genericValue = new GenericValue<String>("weiwei");
        System.out.println(genericValue.getValue());
        GenericValue<Date> genericValue1 = new GenericValue<Date>(new Date());
        System.out.println(genericValue1.getValue());

    }
}

執行結果:
weiwei
Sat Feb 24 15:26:25 CST 2018

泛型類使用,記住一點在使用的時候(宣告和例項化的時候)具體指定引數型別。
還有一點說明,定義了一個泛型類,我們在使用的時候指定具體型別,主要作用給編譯器在編譯java原始碼的時候使用,可以起到型別安全檢查,和型別強制轉換程式碼的生成。但記住我們在使用的時候,也可以不指定具體型別(其實也就是放棄了使用泛型,也就享受不到泛型帶來的好處了),這時候,程式碼也是可以編譯通過執行的,如下面例子。

public class TestGeneric {

    public static void main(String[] args) {

        GenericValue genericValue = new GenericValue("weiwei");
        System.out.println(genericValue.getValue());
        GenericValue genericValue1 = new GenericValue(new Date());
        System.out.println(genericValue1.getValue());

    }
}

執行結果:
weiwei
Sat Feb 24 15:26:25 CST 2018

泛型方法

    /**泛型方法
     * @param T 宣告一個泛型型別
     * @param c 用來建立泛型物件
     * @return T 返回一個泛型型別的返回值
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
     //<T> 此作用說明該方法為泛型方法
     //引數(Class<T>)指定此方法的具體泛型型別
    public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException{ 
        T t = c.newInstance();
        return t;
    }

測試程式碼TestGeneric.java檔案

package generics;

import java.util.Date;


class Peoples{
    private String name = "張三";

    public void showName(){

        System.out.println(name);
    }

}

//在例項化泛型類時,必須指定V的具體型別
//此處V可以寫成任意符合java規則的標識
class GenericValue <V>{
     //value這個成員變數的型別為V,V的型別由外部指定  
    private V value;
    //泛型構造方法,形參v的型別由該類具體使用時指定。
    public GenericValue(V v){
        this.value = v;
    }
    //泛型方法 返回值由該類具體使用時指定。
    public V getValue(){

        return  value;
    }


    /**泛型方法
     * @param T 宣告一個泛型型別
     * @param c 用來建立泛型物件
     * @return T 返回一個泛型型別的返回值
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException{ 
        T t = c.newInstance();
        return t;
    }

}



public class TestGeneric {

    public static void main(String[] args) {

        GenericValue <String>genericValue = new GenericValue<String>("weiwei");
        try {
            Peoples people = genericValue.getObject(Peoples.class);
            people.showName();

        } catch (InstantiationException | IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }
}

執行結果:張三

靜態泛型方法

import java.util.ArrayList;
import java.util.List;

public class TestGeneric {

    //列印容器泛型方法
    public static <T> void printList(List<T> t){

        for(T tt:t){
            System.out.print(tt+"\t");
        }
        System.out.println();

    }


    public static void main(String[] args) {

        List<Integer> intList = new ArrayList<Integer>();
        List<String> stringList = new ArrayList<String>();
        for(int i =0;i<10;i++){
            intList.add(i);
            stringList.add("weiwei"+i);

        }
        //列印list容器
        printList(intList);
        printList(stringList);



    }
}

執行結果:
這裡寫圖片描述

泛型方法能使方法獨立於類而產生變化,對於泛型方法的使用有一個指導原則:如果你能做到,你就該儘量使用泛型方法。也就是說,如果使用泛型方法將整個類泛型化,那麼就應該使用泛型方法。對於一個static的方法,無法訪問泛型型別的引數。所以如果static方法要使用泛型能力,就必須使其成為泛型方法

希望對您有所幫助!