java中的泛型【T】與通配符【?】概念入門


使用泛型的目的是利用Java編譯機制,在編譯過程中幫我們檢測代碼中不規范的有可能導致程序錯誤的代碼。例如,我們都知道List容器可以持有任何類型的數據,所以我們可以把String和Integer等類型同時放入同一個List容器中,但是這種做法是極其危險的。在泛型機制中,這種操作就會導致編譯不通過,會強制要求你將List容器中的數據類型修改為統一類型。這種機制可以幫助我們減少程序運行中隱藏的Bug。

泛型【T】

泛型在代碼中使用廣泛。

泛型的用法

根據泛型使用的位置,即使用在類(Class),屬性(Field)和方法(Method)的不同位置,可以將泛型的用法總結為以下幾種:

1.泛型類。在類名后添加泛型標識(<T...>),用於表示該類持有的一種類型。

2.泛型屬性。泛型屬性必須結合泛型類使用,用於接收泛型類持有的類型T。

3.泛型方法。在方法的返回值前聲明泛型(<T extends 父類名>),該泛型是對該方法的參數T的一種限定。

泛型用法的補充

1.如果泛型T沒有被extends修飾(包括類和方法),我們稱之為無界泛型;如果被extend修飾,我們就稱之為有界泛型。

2.如果方法參數中有泛型T,而方法的返回類型前沒有泛型T,該類型就不是泛型方法,而是泛型類。

3.泛型方法常用在工具類中(即該方法只是一種工具),與類的實例對象無關。

4.當泛型方法中的泛型T與類中的泛型T同名時會產生警報,因為編譯器不確定你要使用哪個持有對象。

有界泛型

相較於無界泛型(沒有限定類型)的用法,我們可以使用有界泛型(<T extends 父類名>)來限定持有對象的范圍,或泛型方法傳入該方法參數的范圍,以此保證業務邏輯的正確執行。

有界泛型只有上界(extends),而沒有下界的用法(相對於通配符【?】)。

泛型中的繼承

用一段代碼說明泛型中繼承的使用。

ArrayList<String> arrayList = new ArrayList<>();
Object object = new Object();
arrayList.add(object); // 編譯器報錯

因為 ArrayList<String>不是 ArrayList<Object>的子類 ,因此編譯器會報錯,錯誤信息為The method add(String) in the type ArrayList<String> is not applicable for the arguments (Object)。

通配符【?】

通配符代表的是一種未知的類型,常用在方法上(注意與泛型方法的區別,其不需要在方法的返回類型前聲明。

上界通配

上界通配即定義通配符的上界,用關鍵字extends聲明。

public static void process(List<? extends Foo> list){}

無界通配

無界通配即不限制通配符的界限,不需要任何關鍵字去修飾【?】。

public static void printList(List<?> list){}

通配符的功能形式和聲明類型為Object是有區別的。

public static void printList(List<Object> list){}

List<Object>和List<?>是不一樣的,前者可以插入Object或任何Object對象的子類,但是后者在類型不匹配的情況下只能夠插入null。

public class TestWildCard {
    public void printList(List<String> list) {
        for (Object elem : list)
            System.out.print(elem + " ");
        System.out.println();
    }

    public void printList2(List<?> list) {
        for (Object elem : list)
            System.out.print(elem + " ");
        System.out.println();
    }

    public static void main(String[] args) {
        TestWildCard testWildcard = new TestWildCard();

        ArrayList<? extends Object> arrayList = new ArrayList<>();
        arrayList.add(null);
        // arrayList.add(testWildcard); // 報錯

        ArrayList<Object> arrayList2 = new ArrayList<>();
        arrayList2.add(null);
        arrayList2.add("2");

        List<Integer> li = Arrays.asList(1, 2, 3);
        testWildcard.printList2(li);
        // testWildcard.printList(li); // 報錯

        List<String> ls = Arrays.asList("one", "two", "three");
        testWildcard.printList2(ls);
        testWildcard.printList(ls);
    }
}

下界通配

下界通配即定義通配符的下界,用關鍵字super聲明。

public static void addNumbers(List<? super Integer> list){}

泛型【T】與通配符【?】的區別

泛型和通配符最根本的區別就是Java編譯器會把泛型【T】推斷成具體類型,而把通配符【?】推斷成未知類型。Java編輯器只能操作具體類型,不能操作未知類型,如果有對參數做修改的操作就必須要使用泛型,如果僅僅是查看就可以使用通配符。

這樣,我們可以利用通配符特性設計出安全的接口。比如在一個接口的方法中定義了通配符參數,則繼承該接口的所有方法都不能修改該方法傳遞過來的參數。

interface GInterface {
    <T> void foo(List<? extends T> list);
}

public class GInterfaceImpl implements GInterface{
    @Override
    public <T> void foo(List<? extends T> list) {
        // 只能遍歷list,不能修改list
        for (T t : list) {
            System.out.println(t);
        }

        // list.add(new Object()); // 編譯器報錯
    }

    public static void main(String[] args) {
        GInterfaceImpl gIterfaceImpl = new GInterfaceImpl();
        ArrayList<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("8");
        gIterfaceImpl.foo(list);
    }
}

 

"你弱的時候,壞人最多,這個世界的溫柔,都來自與你的強大。"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM