Java 泛型(一) 泛型使用基礎


泛型Generics

  

  所謂泛型,就是變量類型的參數化。

  泛型是JDK1.5中一個最重要的特征。通過引入泛型,我們將獲得編譯時類型的安全和運行時更小的拋出ClassCastException的可能。

  在JDK1.5中,你可以聲明一個集合將接收/返回的對象的類型。

  使用泛型時如果不指明參數類型,即泛型類沒有參數化,會提示警告,此時類型為Object。

 

為什么使用泛型

  使用泛型的典型例子,是在集合中的泛型使用。

  在使用泛型前,存入集合中的元素可以是任何類型的,當從集合中取出時,所有的元素都是Object類型,需要進行向下的強制類型轉換,轉換到特定的類型

  比如:

List myIntList = new LinkedList(); // 1

myIntList.add(new Integer(0)); // 2

Integer x = (Integer) myIntList.iterator().next(); // 3   

  第三行的這個強制類型轉換可能會引起運行時的錯誤。

  泛型的思想就是由程序員指定類型,這樣集合就只能容納該類型的元素。

  使用泛型:

List<Integer> myIntList = new LinkedList<Integer>(); // 1'

myIntList.add(new Integer(0)); // 2'

Integer x = myIntList.iterator().next(); // 3'

  將第三行的強制類型轉換變為了第一行的List類型說明,編譯器會為我們檢查類型的正確性。這樣,代碼的可讀性和健壯性也會增強。

 

泛型使用基礎

  例如:

public interface List <E> 
{
    void add(E x);
    Iterator<E> iterator();
}

public interface Iterator<E> 
{
    E next();
    boolean hasNext();
}

 

  尖括號中包含的是形式類型參數formal type parameters),它們就如同一般的類型一樣,可以在整個類的聲明中被使用。

  當類被使用時,會使用具體的實際類型參數actual type argument代替。

  比如前面的例子中的List<Integer>,那么所有的E將會被Integer類型所代替。

  泛型類型參數只能被類或接口類型賦值,不能被原生數據類型賦值,原生數據類型需要使用對應的包裝類。

  形式類型參數的命名:盡量使用單個的大寫字母(有時候多個泛型類型時會加上數字,比如T1,T2),比如許多容器集合使用E,代表element(元素),Map中用K代表鍵keys,V代表值。

 

泛型容器的實現討論

  不能用new的形式來創建一個泛型數組。 

  如下:

public class SimpleCollection<T>
{
   private T[] objArr;
   private int index = 0;

   public SimpleCollection()
   {
      //Error: Cannot create a generic array of T
      objArr = new T[10];   
   }
}

  會報錯。

 

  如何創建一個數組讓它接受所有可能的類型呢?

public class SimpleCollection<T>
{
    private T[] objArr;
    
    private int index = 0;
    public SimpleCollection()
    {
        //Error: Cannot create a generic array of T
        //objArr = new T[10];
        
        //Warning: Unchecked cast from Object[] to T[]
        objArr = (T[]) new Object[10];
        
    }

}

 

  這個形式雖然可以做到,但是會產生一個警告

  查看ArrayList中的實現,可以發現它是使用了一個Object類型的數組:

private transient Object[] elementData;

 

  在取出的時候(get方法中)使用了類型轉換:

(E) elementData[index];

 

泛型和子類

List<String> ls = new ArrayList<String>(); // 1

List<Object> lo = ls; // 2

 

  一個String類型的List是一個Object類的List嗎?

  不可以,Java編譯器將會在第二行產生一個編譯錯誤,因為它們的類型不匹配。

  這樣就避免了如果lo引入加入Object類型的對象,而ls引用試圖將其轉換為String類型而引發錯誤。所以編譯器阻止了這種可能。

 

繼承泛型類別

  直接用例子說明:

  父類:

public class Parent<T1,T2>
{
    private T1 foo1;
    private T2 foo2;
    
    public T1 getFoo1()
    {
        return foo1;
    }
    public void setFoo1(T1 foo1)
    {
        this.foo1 = foo1;
    }
    public T2 getFoo2()
    {
        return foo2;
    }
    public void setFoo2(T2 foo2)
    {
        this.foo2 = foo2;
    }    

}

 

  子類繼承父類:

public class Child<T1, T2, T3> extends Parent<T1, T2>
{
    private T3 foo3;

    public T3 getFoo3()
    {
        return foo3;
    }

    public void setFoo3(T3 foo3)
    {
        this.foo3 = foo3;
    }
    
}

 

實現泛型接口

  見例子:

  泛型接口:

public interface ParentInterface<T1,T2>
{
    public void setFoo1(T1 foo1);
    public void setFoo2(T2 foo2);
    public T1 getFoo1();
    public T2 getFoo2();

}

  子類實現泛型接口:

public class ChildClass<T1,T2> implements ParentInterface<T1, T2>
{
    private T1 foo1;
    private T2 foo2;
    
    @Override
    public void setFoo1(T1 foo1)
    {
        this.foo1 = foo1;
        
    }
    @Override
    public void setFoo2(T2 foo2)
    {
        this.foo2 = foo2;
    }
    @Override
    public T1 getFoo1()
    {
        return this.foo1;
    }
    @Override
    public T2 getFoo2()
    {
        return this.foo2;
    }

}

 

參考資料:

  聖思園張龍老師Java SE視頻教程。

  The Java Tutorials : Lesson: Generics (Updated)

  http://docs.oracle.com/javase/tutorial/java/generics/index.html

  Lesson: Generics

  http://docs.oracle.com/javase/tutorial/extra/generics/index.html

 


免責聲明!

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



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