1. 程式人生 > >corajava--簡單總結java泛型的應用

corajava--簡單總結java泛型的應用

原文以及參考部落格:

泛型的定義

按照百度百科的介紹,泛型是Java SE 1.5的新增特性,泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。泛型是具有佔位符(型別引數)的類、結構、介面和方法,這些佔位符是類、結構、介面和方法所儲存或使用的一個或多個型別的佔位符。泛型集合類可以將型別引數用作它所儲存的物件的型別的佔位符;型別引數作為其欄位的型別和其方法的引數型別出現。泛型方法可以將其型別引數用作其返回值的型別或者其形參的型別之一。

在Java SE 1.5之前,沒有泛型的情況的下,通過對型別Object的引用來實現引數的“任意化”,“任意化”帶來的缺點是要做顯式的強制型別轉換,而這種轉換是要求開發者對實際引數型別可以預知的情況下進行的。對於強制型別轉換錯誤的情況,編譯器可能不提示錯誤,在執行的時候才出現異常,這是一個安全隱患。泛型的好處是在編譯的時候檢查型別安全,並且所有的強制轉換都是自動和隱式的,以提高程式碼的重用率。

java中泛型的特點

1、型別安全。型別錯誤現在在編譯期間就被捕獲到了,而不是在執行時當作java.lang.ClassCastException展示出來,將型別檢查從執行時挪到編譯時有助於開發者更容易找到錯誤,並提高程式的可靠性

2、消除了程式碼中許多的強制型別轉換,增強了程式碼的可讀性

3、泛型使用之後不會對一個物件的例項造成影響。所以無法通過定義不同的泛型去定義過載的方法。

泛型中E、T、?等識別符號的區別及用法

其實使用java26個大寫字元都可以,只不過使用以下的幾個更有助於不同程式設計師之間的理解。比將T換成了A,在執行效果上是沒有任何區別的,只不過我們約定好了T代表type,所以還是按照約定規範來比較好

,增加了程式碼的可讀性。

 Java泛型中的標記符含義: 

 E - Element (表示元素 在集合中使用,因為集合中存放的是元素) 

 T - Type(Java 類) 表示一個具體的java型別,多個引數時可以使用T1、T2、T3...... 

 K - Key(鍵)

 V - Value(值)

 N - Number(數值型別)

? -  表示不確定的java型別,用作型別萬用字元,表示任何型別的父型別。

 S、U、V  - 2nd、3rd、4th types

泛型約束

可以使用extends、super,此時extends不表示繼承。它表示型別萬用字元的上限。假設有一組類繼承關係C繼承自B,B繼承自A。如下程式碼所定義的TestConstraint類:建立對應的例項時,只能是B及其B型別的子類(C類)


public class TestExtends {
    public static void main(String[] args) {
        TestConstraint<B> bTestConstraint = new TestConstraint<B>();
        TestConstraint<C> cTestConstraint = new TestConstraint<C>();
       
        //使用A型別時,此時編譯不通過:Type parameter 'com.songxl.generic.A' is not within its bound; should extend 'com.songxl.generic.B'
        TestConstraint<A> aTestConstraint = new TestConstraint<A>();

        //使用其他型別時也會編譯報錯:Cannot resolve symbol 'T'
        TestConstraint<T> tTestConstraint = new TestConstraint<T>();
    }
}

class TestConstraint <T extends B>{}

class A {}
class B extends A{}
class C extends B{}

相對應的還有型別萬用字元的下限:super。同樣藉助於上述程式碼,在一個方法行參中測試super的使用。表示傳遞引數時:只能是B型別或者是B型別的父型別。

public class TestExtends {
    public static void main(String[] args) {
        TestConstraint<A> aTestConstraint = new TestConstraint<A>();
        TestConstraint<B> bTestConstraint = new TestConstraint<B>();
        TestConstraint<C> cTestConstraint = new TestConstraint<C>();

        //如果使用B型別或者B型別的父型別作為引數時,就沒有問題
        getData(aTestConstraint);
        getData(bTestConstraint);

        //如果使用C型別(B型別的子類)的引數,編譯會報錯
        getData(cTestConstraint);

        //如果使用其他型別的實參,也會編譯報錯的
        getData(13);
    }

    public static void getData(TestConstraint<? super B> data) {
        System.out.println("data:" + data);
    }
}

class TestConstraint<T extends A> { }
class A { }
class B extends A { }
class C extends B { }

泛型的幾種使用情況比較

  • List<T>,List<Object>,List<?>區別

ArrayList<T> al=new ArrayList<T>(); 指定集合元素只能是T型別(例項化的時候需要將其定義為所需的型別。比如:ArrayList<String> list = new ArrayList<String>())。

Object和T不同點在於,Object是一個實際的類,並沒有泛指誰,而T可以泛指Object,比方public void printList(List<T> list){}方法中可以傳入List<Object> list型別引數,也可以傳入List<String> list型別引數,但是public void printList(List<Object> list){}就只可以傳入List<Object> list型別引數,如果傳入List<String> list型別的引數就會報錯。

?和T區別是?是一個不確定類,?和T都表示不確定的型別 ,但如果是T的話,函式裡面可以對T進行操作,例如: T car = getCar(),而不能用? car = getCar()。

  • 在定義成員變數時

    //此時使用T時會報錯:Cannot resolve symbol 'T',因為Class類在定義時使用了public final class Class<T>   表示此時需要指定具體的型別。
    private Class<T> t;
    //如果不確定用?,但是在例項化使用時需要指定具體的型別。
    private Class<?> t2;
  • 在定義方法的引數時

  public static void printList1(List<Object> list) {
        for (Object o : list) {
            System.out.print(o+" .");
        }
        System.out.println();
    }

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

    public static <T> T printList3(List<T> list) {
        for (T t : list) {
            T t1 = t;
            System.out.print(t+" .");
        }
        System.out.println();
        T t = list.get(0);
        return t;
    }

    public static void getData(TestConstraint<? super B> data) {
        System.out.println("data:" + data);
    }

    public static void getData2(TestConstraint<? extends B> data) {
        System.out.println("data:" + data);
    }
  • 在類上定義時

//假定有:
class A { }
class B extends A { }
class C extends B { }

//可以這麼定義:
class Test<String>{}
class Test2<T>{}
class Tes4<T extends B>{}

//如下情況會報錯:
//'class' or 'interface' expected
class Tes5<T super B>{}

//Unexpected wildcard
class Test3<?>{}

//具體案例
public class ArrayList<E>{}
public class HashMap<K,V>{}
public class ThreadLocal<T>{}

暫時瞭解到的只有這些,後續接觸到了再補充。

因為LZ也是在網上查閱各位大佬的資料及自己的理解編的,在開頭已經貼出原文地址,如果涉及侵權,請聯絡本人刪除。