使用Java中的泛型

1.定義泛型接口和類

  泛型接口:

public interface List<E>
{
	void add(E x);
	Iterator<E> iterator();
	...
}
public interface Iterator<E>
{
	E next();
	boolean hasNext();
	...
}
public interface Map<K,V>
{
	Set<K,V> keySet();
	V put(K key,V value);
	...
}
  使用泛型的類:

import java.util.*;
class Genericclass<T>
{
	private T info;
	public Genericclass(){}
	public Genericclass(T t)
	{//使用泛型的構造器
		this.info=t;
	}
	public void setinfo(T info)
	{
		this.info=info;
	}
	public T getinfo()
	{
		return this.info;
	}
	public static void main(String[] args) 
	{
		//調用使用泛型的構造器
		Genericclass<String> gs=new Genericclass<>("123");
		System.out.println(gs.getinfo());
		Genericclass<Double> gd=new Genericclass<>(3.45);
		System.out.println(gd.getinfo());
	}
}
2.從泛型類派生子類

import java.util.*;
class Genericclass<T>
{
	private T info;
	public Genericclass(){}
	public Genericclass(T t)
	{//使用泛型的構造器
		this.info=t;
	}
	public void setinfo(T info)
	{
		this.info=info;
	}
	public T getinfo()
	{
		return this.info;
	}
	
}
/*定義Genericclass的子類
  使用public class A1 extends Genericclass<String>
  或者public class A1 extends Genericclass方式,不能使用
  public class A1 extends Genericclass<T>,即必須指定具體的類型或者什麼都不指定
*/
public class A1 extends Genericclass<String>
{
	public A1(String s)
	{
		super(s);
	}
	public String getinfo()
	{
		return "子類:"+super.getinfo();
	}
	public static void main(String[] args) 
	{
		//調用使用泛型的構造器
		A1 a1=new A1("123");
		System.out.println(a1.getinfo());
	}
}

3.類型通配符

  3.1爲什麼使用類型通配符?如下:

import java.util.*;
class ArrErr 
{
	public void test(List c)
	{
		for(int i=0;i<c.size();i++)
		{
			System.out.println(c.get(i));
		}
	}
    /*public void test(List<Object> c)
	{
		for(int i=0;i<c.size();i++)
		{
			System.out.println(c.get(i));
		}
	}*/
	public static void main(String[] args) 
	{
		List<String> strList=new ArrayList<>();
		ArrErr ae=new ArrErr();
		/*調用第一個test時不會有編譯問題,但調用第二個時會產生編譯錯誤
		  由於List<String>不是List<Object>的子類,二者不能進行轉換
		  而一般情況下List是泛型接口,使用時爲了能夠知道其中元素的類型
		  往往使用第二種方法定義,這就限制了第二種方法的使用,必須完全轉爲Object
		  類型才能使用
		*/
		ae.test(strList);
		System.out.println("Hello World!");
	}
}
  3.2使用類型通配符

    通過?符號作爲參數進行傳遞,即可以與任何類型進行匹配的類型。則上述的test函數可改爲:

public void test(List<?> c)
	{
		for(int i=0;i<c.size();i++)
		{
			System.out.println(c.get(i));
		}
	}
//這種定義只能訪問c中的元素,不能修改和添加
List<?> c= new ArrayList<String>();
因爲此時不知道c集合中元素的類型,只表明它是各種泛型List的父類,即可以將List<String>類型的參數傳遞給List<?>類型的參數。

一般通配符是和泛型結合使用,如下(使用通配符的上限):

public void test(List<? extends E> c)
	{
		for(int i=0;i<c.size();i++)
		{
			System.out.println(c.get(i));
		}
	}
傳遞給該函數的參數是泛型參數E的子類,或者在泛型方法中使用。

4.泛型方法

  4.1爲什麼使用泛型方法?如下:

import java.util.*;
class GenMethod 
{
	/*將一個Object數組的元素添加到Collection集合中
	static void fromarraytocollection(Object[] a,Collection<Object> c)
	{
		for(Object o:a)
		{
			c.add(o);
		}
	}*/
	//使用泛型方法定義
	static <T> void  fromarraytocollection(T[] a,Collection<T> c)
	{
		for(T o:a)
		{
			c.add(o);
		}
	}
	public static void main(String[] args) 
	{
		String[] str={"a","b"};
		List<String> slist=new ArrayList<>();
		GenMethod gm=new GenMethod();
		/*使用第一個方法時,下面將會出現編譯錯誤,因爲List<String>不是
		  Collection<Object>的子類,不能進行轉換也不能使用通配符?,
		  因爲需要向c中添加元素,?不知道元素的類型
		*/
		fromarraytocollection(str,slist);
		Object[] obj=new Object[100];
		List<Object> lo=new ArrayList<>();
		fromarraytocollection(obj,lo);
	}
}
  4.2泛型方法的定義方式:修飾符 <T,S> 返回值類型  方法名(形參列表){}。

       大多數情況下可以使用泛型方法來代替類型通配符,如下:

public interface Collection<E>
{
	boolean containsAll(Collection<?> c);
	boolean addAll(Collection<? extends E> c);
	...
}
//轉爲泛型方法爲:
public interface Collection<E>
{
	<T> boolean containsAll(Collection<T> c);
	<T extends E> boolean addAll(Collection<T> c);
	...
}

  注意:如果一個方法的類型形參(a)的類型或返回值的類型依賴於另一個形參(b)的類型,則形參(b)的類型聲明不應該使用通配符,因爲如果b的類型不確定,則a的類型也不確定,只能使用類型形參,即泛型方法。

  4.3設定通配符的下限

    爲什麼要設置下限?如下:

public static<T> T copy(Collection<T> dest,Collection<? extends T> src)
	{
		T last=null;
		for(T t:src)
		{
			last=t;
			dest.add(last);
		}
		/*返回一個目標集合類型的元素,但實際傳遞的元素類型爲T的子類,此時就會忘記原集合的類型
		  而src原類型必須是T的子類,所以需要使用<? extends T>的方法定義
		*/
		return last;
	}
	//使用下述代碼訪問時,則會出現編譯錯誤
	List<Number> lm=new ArrayList<>();
	List<Integer> li=new ArrayList<>();
	Integer last=copy(lm,li);//此時想反悔Integer類型的參數,但函數中返回的是Number類型
    則只需將上述方法改爲:public static<T> T copy(Collection<? super T> dest,Collection<T> src)

    即將dest類型使用通配符表示,設置其下限必須是T類型,及其父類類型。

   注:泛型方法可進行方法重載,如:

  public static<T> void copy(Collection<T> dest,Collection<? extends T> src);
  public static<T> T void copy(Collection<? super T> dest,Collection<T> src);

5.擦出和轉換

  當把一個具有泛型信息的對象賦值給另一個沒有泛型信息的變量時,所有尖括號之間的類型信息都被扔到,比如把一個List<String>類型轉爲List,則該List集合元素的類型都轉爲類型變量的上限,無法進行還原。比如:

 A<Integer> a=new A<>(5);
    Integer as=a.get();//獲取5的值
    A b=a;
       Object ob=b.get();//此時b只知道a中的5被轉爲Object類型
    //無法再將b中的5隱式轉爲Integer
    Integer ib=b.get();
但下面的這種方式是正確的:
    List<Integer> li=new ArrayList<>();
    List l=li;
    List<String> ls=l;//可以轉換,但當訪問ls中的數組時,會出現運行時異常

6.泛型和數組

    java5中的泛型有一個重要的原則:如果一段代碼在編譯時沒有提出"[unchecked]"未經檢查的異常,則在運行時也不會拋出該異常。  

import java.util.*;
class GenArray 
{
	public static void main(String[] args) 
	{
		下面這句在編譯時會引發類型異常
		List<String>[] lsa=new ArrayList[10];//不能使用List<String>[] lsa==new List<String>[10],編譯時不會引發異常,運行時引發異常,未被java設計原則
		Object[] oa =(Object[])lsa;
		List<Integer> li=new ArrayList<Integer>();
		li.add(new Integer(3));
		oa[1]=li;
		String s=lsa[1].get(0);//在運行時將會引發類型異常,不能講Object再轉爲String
		
		//可將第一句改爲如下形式
		List<?>[] lsa =new ArrayList<?>[10];
		//在進行轉換時使用下述方法,則不會出現任何錯誤
		Object target=lsa[1].get(0);
		if(target instanceof String)
		{
			String s=(String)target;
		}
		
	}
}



  




  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章