Java不可不知的泛型使用

安全劍客發表於2020-08-18
泛型在Java中也是個很重要的知識點,本文主要講解基礎的概念,泛型:把型別明確的工作推遲到建立物件或呼叫方法的時候才去明確的特殊的型別。
為什麼使用泛型

看下面一個例子:

為了說明問題,本類寫的儘量簡陋,請把目光主要放在型別上。

public class MyArrayList {
    private int[] elementData;
    private int size = 0;
    public MyArrayList(int capacity) {
        elementData = new int[capacity];
    }
    
	//向陣列中新增元素
    public void add(int i) { 
        if (size == elementData.length) {
            throw new IndexOutOfBoundsException("陣列已滿");
        }
        elementData[size++] = i;
    }
    
	//從陣列中根據下標獲取元素
    public int get(int index) { 
        if (index < 0 || index > size - 1) {
            throw new IndexOutOfBoundsException("超出範圍");
        }
        return elementData[index];
    }
    @Override
    public String toString() {
        return "MyArrayList{" +
                "elementData=" + Arrays.toString(elementData) +
                '}';
    }
}

該類很簡單:有兩個成員變數,elementData是一個陣列,size是陣列中元素的數量。add和get方法能新增和獲取元素。

下面測試一下:

public class Test {
    public static void main(String[] args) {
        MyArrayList myArrayList = new MyArrayList(4);
        myArrayList.add(111); //向陣列中新增3個int元素
        myArrayList.add(222);
        myArrayList.add(333);
        int i = myArrayList.get(0); //獲取
        System.out.println(i);
		//以上正常執行
        myArrayList.add("行小觀"); //新增一個String元素,型別不匹配,報錯
    }
}

向陣列中新增3個int型別的元素並能獲取,這沒問題。

但是如果我們的場景不再需要int型別的元素,而是需要String型別的,那怎麼辦?

很顯然,繼續使用該類會報錯,報錯的原因很簡單:我們向陣列中新增的元素是String型別的,而陣列和方法引數型別是int型別。

此時,就得需要再寫一份程式碼,該份程式碼較之前的並無大修改,只是把int改為String。如果場景繼續變怎麼辦?那就再寫一份新程式碼!

這樣太麻煩了!有沒有解決辦法?有!

我們知道,Object類是所有類的父類,Object型別的變數能夠引用任何型別的物件。所以可以將型別改為Object:

public class MyArrayList {
    private Object[] elementData; 
    private int size = 0;
    public MyArrayList(int capacity) {
        elementData = new Object[capacity];
    }
    public void add(Object o) { //向陣列中新增元素
        if (size == elementData.length) {
            throw new IndexOutOfBoundsException("陣列已滿");
        }
        elementData[size++] = o;
    }
    public Object get(int index) { //從陣列中獲取元素
        if (index < 0 || index > size - 1) {
            throw new IndexOutOfBoundsException("超出範圍");
        }
        return elementData[index];
    }
    @Override
    public String toString() {
        return "MyArrayList{" +
                "elementData=" + Arrays.toString(elementData) +
                '}';
    }
}

再測試一下:

public class Test {
    public static void main(String[] args) {
        //myArrayList 給int元素使用
        MyArrayList myArrayList = new MyArrayList(4);
        myArrayList.add(111); //向陣列中新增3個int元素
        myArrayList.add(222);
        myArrayList.add(333);
        int i = (int) myArrayList.get(0); //獲取
        System.out.println(i);
        //myArrayList 給String元素使用
        MyArrayList myArrayList1 = new MyArrayList(4);
        myArrayList1.add("aaa");
        myArrayList1.add("bbb");
        myArrayList1.add("ccc");
        String str = (String) myArrayList1.get(1);
        System.out.println(str);
    }
}

發現可以向陣列中新增和獲取int或String型別的元素,這證明該類的陣列和方法同時對各種型別的資料都有用,不必再新增額外程式碼。

但是這樣又出現了兩個問題:

第一:從陣列中獲取元素時,需要強制轉換型別才行。

int i = (int) myArrayList.get(0);

第二:同一個陣列可以新增各種型別的元素。

myArrayList.add(111); //int型別
myArrayList.add("222"); //String型別
myArrayList.add(true); //布林型別

這就導致了當我們從陣列中獲取某個元素時,很難知道它的確切型別,往往會強轉型別失敗。

 int i = (int)myArrayList.get(1); //本來是String型別的值,但我提前不知道,拿int變數接收,報錯

那這個問題有沒有解決辦法呢?

有!用泛型!

泛型類

使用泛型改造MyArrayList:

 public class MyArrayList{
    private T[] elementData;
    private int size = 0;
    public MyArrayList(int capacity) {
        elementData = (T[]) new Object[capacity];
    }
    public void add(T o) { //向陣列中新增元素
        if (size == elementData.length) {
            throw new IndexOutOfBoundsException("陣列已滿");
        }
        elementData[size++] = o;
    }
    public T get(int index) { //從陣列中獲取元素
        if (index < 0 || index > size - 1) {
            throw new IndexOutOfBoundsException("超出範圍");
        }
        return elementData[index];
    }
    @Override
    public String toString() {
        return "MyArrayList{" +
                "elementData=" + Arrays.toString(elementData) +
                '}';
    }
}

測試:

 public class Test {
    public static void main(String[] args) {
        //myArrayList 給int元素使用
        MyArrayListmyArrayList = new MyArrayList<>(4);
        myArrayList.add(111); //向陣列中新增3個int元素
//        myArrayList.add("222"); //新增非Integer元素報錯
        int i = myArrayList.get(1); //無需強制轉型
        System.out.println(i);  
    }
}

經過改造,我們把MyArrayList類改為了一個泛型類,它是一個具有多個型別變數的類。

泛型類的宣告方式:引入一個型別變數,如T,然後使用<>將其括起來,放在類名後。

 public class MyArrayList{
    //......
}

如何理解型別變數?它就類似於數學中函式中的變數x,用來代替具體的值:

 f(x) = 3x + 1

型別變數的名稱可以隨便取,一般使用大寫字母表示,比如E、K、V、T等。

泛型類中的成員變數、方法引數和返回值的型別都使用型別變數代替:

 private T[] elementData;
public void add(T o) {
    //.......
}
public T get(int index) {
	//......
}

當然,一個泛型類可以有多個型別變數:

 public class MyClass{
    //......
}

當我們需要例項化泛型類時,就使用具體的型別來替換型別變數(T):

 MyArrayListmyArrayList = new MyArrayList<>(4);

該過程就相當於向數學函式中代入數值:

 f(3) = 3*3+1 = 10
泛型方法

當我們宣告瞭一個泛型類後,可以很自然地在類內部使用泛型方法。

其實,當類是普通類時,我們仍然能夠使用泛型方法。下面是一個例子:

 public class PrinterVar {
    //該方法接收一個T型別的變數,列印並返回該變數
    publicT print(T var) {
        System.out.println(var);
        return var;
    }
    public static void main(String[] args) {
        PrinterVar printerVar = new PrinterVar();
        String var = printerVar.print("aa");//String型別
        Integer var1 = printerVar.print(12);//int型別
        System.out.println(var + " " + var1);
    }

原文地址:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559985/viewspace-2712662/,如需轉載,請註明出處,否則將追究法律責任。

相關文章