1. 程式人生 > 其它 >泛型方法的定義和使用

泛型方法的定義和使用

泛型的作用與定義

型別的引數化,就是可以把型別像方法的引數那樣傳遞

泛型使編譯器可以在編譯期間對型別進行檢查以提高型別安全,減少執行時由於物件型別不匹配引發的異常。

1. 泛型是什麼


一說到泛型,大夥肯定不會陌生,我們程式碼裡面有很多類似這樣的語句:

List list=new ArrayList<>();

ArrayList就是個泛型類,我們通過設定不同的型別,可以往集合裡面儲存不同型別的資料型別(而且只能儲存設定的資料型別,這是泛型的優勢之一)。“泛型”簡單的意思就是泛指的型別(引數化型別)。想象下這樣的場景:如果我們現在要寫一個容器類(支援資料增刪查詢的),我們寫了支援String型別的,後面還需要寫支援Integer型別的。然後呢?Doubel、Float、各種自定義型別?這樣重複程式碼太多了,而且這些容器的演算法都是一致的。我們可以通過泛指一種型別T,來代替我們之前需要的所有型別,把我們需要的型別作為引數傳遞到容器裡面,這樣我們演算法只需要寫一套就可以適應所有的型別。最典型的的例子就是ArrayList了,這個集合我們無論傳遞什麼資料型別,它都能很好的工作。

聰明的同學看完上面的描述,靈機一動,寫出了下面的程式碼:

class MyList{    private Object[] elements=new Object[10];    private int size;    public void add(Object item) {
        elements[size++]=item;
    }    public Object get(int index) {        return elements[index];
    }
}

這個程式碼靈活性很高,所有的型別都可以向上轉型為Object類,這樣我們就可以往裡面儲存各種型別的資料了。的確Java在泛型出現之前,也是這麼做的。但是這樣的有一個問題:如果集合裡面資料很多,某一個數據轉型出現錯誤,在編譯期是無法發現的。但是在執行期會發生java.lang.ClassCastException。例如:

MyList myList=new MyList();
myList.add("A");
myList.add(1);
System.out.println(myList.get(0));
System.out.println((String)myList.get(1));

我們在這個集合裡面儲存了多個型別(某些情況下容器可能會儲存多種型別的資料),如果資料量較多,轉型的時候難免會出現異常,而這些都是無法在編譯期得知的。而泛型一方面讓我們只能往集合中新增一種型別的資料,同時可以讓我們在編譯期就發現這些錯誤,避免執行時異常的發生,提升程式碼的健壯性。

2. Java泛型介紹

下面我們來介紹Java泛型的相關內容,下面會介紹以下幾個方面:

  • Java泛型類

  • Java泛型方法

  • Java泛型介面

Java泛型類

類結構是面向物件中最基本的元素,如果我們的類需要有很好的擴充套件性,那麼我們可以將其設定成泛型的。假設我們需要一個數據的包裝類,通過傳入不同型別的資料,可以儲存相應型別的資料。我們看看這個簡單的泛型類的設計:

class DataHolder<T>{
    T item;    public void setData(T t) {        this.item=t;
    }    public T getData() {        return this.item;
    }
}

泛型類定義時只需要在類名後面加上型別引數即可,當然你也可以新增多個引數,類似於,等。這樣我們就可以在類裡面使用定義的型別引數。

泛型類最常用的使用場景就是“元組”的使用。我們知道方法return返回值只能返回單個物件。如果我們定義一個泛型類,定義2個甚至3個型別引數,這樣我們return物件的時候,構建這樣一個“元組”資料,通過泛型傳入多個物件,這樣我們就可以一次性方法多個數據了。

Java泛型方法

前面我們介紹的泛型是作用於整個類的,現在我們來介紹泛型方法。泛型方法既可以存在於泛型類中,也可以存在於普通的類中。如果使用泛型方法可以解決問題,那麼應該儘量使用泛型方法。下面我們通過例子來看一下泛型方法的使用:

class DataHolder<T>{
    T item;    public void setData(T t) {        this.item=t;
    }    public T getData() {        return this.item;
    }    /**
     * 泛型方法
     * @param e
     */
    public void PrinterInfo(E e) {
        System.out.println(e);
    }
}

我們來看執行結果:

1AAAAA8.88

從上面的例子中,我們看到我們是在一個泛型類裡面定義了一個泛型方法printInfo。通過傳入不同的資料型別,我們都可以打印出來。在這個方法裡面,我們定義了型別引數E。這個E和泛型類裡面的T兩者之間是沒有關係的。哪怕我們將泛型方法設定成這樣:

//注意這個T是一種全新的型別,可以與泛型類中宣告的T不是同一種類型。public void PrinterInfo(T e) {
    System.out.println(e);
}//呼叫方法DataHolder dataHolder=new DataHolder<>();
dataHolder.PrinterInfo(1);
dataHolder.PrinterInfo("AAAAA");
dataHolder.PrinterInfo(8.88f);

這個泛型方法依然可以傳入Double、Float等型別的資料。泛型方法裡面的型別引數T和泛型類裡面的型別引數是不一樣的型別,從上面的呼叫方式,我們也可以看出,泛型方法printInfo不受我們DataHolder中泛型型別引數是String的影響。我們來總結下泛型方法的幾個基本特徵:

  • public與返回值中間非常重要,可以理解為宣告此方法為泛型方法。

  • 只有聲明瞭的方法才是泛型方法,泛型類中的使用了泛型的成員方法並不是泛型方法。

  • 表明該方法將使用泛型型別T,此時才可以在方法中使用泛型型別T。

  • 與泛型類的定義一樣,此處T可以隨便寫為任意標識,常見的如T、E、K、V等形式的引數常用於表示泛型。


Java泛型介面

Java泛型介面的定義和Java泛型類基本相同,下面是一個例子:

//定義一個泛型介面public interface Generator<T> {    public T next();
}

此處有兩點需要注意:

  • 泛型介面未傳入泛型實參時,與泛型類的定義相同,在宣告類的時候,需將泛型的宣告也一起加到類中。例子如下:
/* 即:class DataHolder implements Generator{
 * 如果不宣告泛型,如:class DataHolder implements Generator,編譯器會報錯:"Unknown class"
 */class FruitGenerator<T> implements Generator<T>{    @Override
    public T next() {        return null;
    }
}
  • 如果泛型介面傳入型別引數時,實現該泛型介面的實現類,則所有使用泛型的地方都要替換成傳入的實參型別。例子如下:
class DataHolder implements Generator<String>{    @Override
    public String next() {        return null;
    }
}

從這個例子我們看到,實現類裡面的所有T的地方都需要實現為String。
轉載出處:https://blog.csdn.net/weixin_39888180/article/details/111104741