Java中泛型的基礎到提高《精簡》

YX_blog發表於2015-08-16

<span style="font-family: SimHei; background-color: rgb(255, 255, 255);"> </span><span style="font-family: SimHei; background-color: rgb(255, 255, 255);">	</span><span style="font-family:SimHei;font-size:24px;color:#ff0000;">泛型初級</span>
<span style="font-family: SimHei; background-color: rgb(255, 255, 255); font-size: 18px;"><span style="white-space:pre">	</span>泛型的由來:</span>

集合中可以儲存任意型別物件,但是在取出時,如果要使用具體物件的特有方法時,

需要進行向下轉型,如果儲存的物件型別不一致,在轉型過程中就會出現ClassCastException異常。

這樣就給程式帶來了不安全性。
在jdk1.5以後就有了解決方案——泛型技術:在儲存元素時,就不允許儲存不同型別的元素。

儲存了就編譯失敗。 所以就需要在儲存元素時,在容器上明確具體的元素型別,這其實和陣列定義很像。

泛型的好處

1)將執行時期的ClassCastException異常轉移到了編譯時期,進行檢查,並以編譯失敗來體現。 這樣有利於程式設計師儘早解決問題。(不會再執行中出現異常,在編譯時候就能夠發現錯誤)(泛型規定不能只能是自己的型別或者是子類) 
2)避免了向下轉型(強轉)的麻煩。

程式碼說明一切:

package cn.hncu.generic;
//泛型能夠限制型別。
public class genericdemo1 {

	public static void main(String[] args) {
			Mygeneric<Worker> generic =new Mygeneric<Worker>();
			generic.add(new Worker());
			//generic.add(00); 自己定義了泛型,只能是Worker型別的
		//	generic.add(new Student());//同樣的報錯,只能新增相同型別的資料
			System.out.println(generic);
			
			Mygeneric<Student> s=new Mygeneric<Student>();
			s.add(new Student());
		//	s.add(new Worker());//編譯通不過,出錯,只能新增泛型定義的資料
			
			
	}

}
class Mygeneric <QQ>{//自己定義泛型,隨便寫寫,最重要的是<>裡面的內容必須與下面的一致,內容隨便寫都可以
	QQ obj;
	
	
	public void add(QQ obj){//由於前面限制,所以只能新增QQ型別的資料
		System.out.println("add:"+obj);
		
	}
	public QQ out(){
		return obj;
	}
	@Override
	public String toString() {
		return "Mygeneric [obj=" + obj + "]";
	}
	
	
	
}
class Worker{
	
}
class Student{
	
}

使用泛型的動機舉例(以集合為例):

對集合中存放元素的型別進行限定,防止後期出錯。如果限定某個集合只能存放Person類物件(因為後期會把元素取出來當作Person物件來處理),這樣放其它型別的物件在編譯時就會報錯。相當於把一個類泛化成很多個不同型別(具體化,限定化)的類。泛型使用的程式碼如:

List<Person> persons = new ArrayList<Person>;
Map<String,String> m = new HashMap<String,String>;

注意:當一個變數被宣告為泛型時,只能被例項變數和方法呼叫,而不能被靜態變數和方法呼叫。原因很簡單,引數化的泛型是一些例項。靜態成員是被類的例項和引數化的類所共享的,所以靜態成員不應該有型別引數和他們關聯。 當一個類要操作的引用資料型別不確定的時候,可以將該型別定義一個形參。用到的這類時,由使用者來通過傳遞型別引數的形式,來確定要操作的具體的物件型別。 什麼時候使用泛型類呢?只要類中操作的引用資料型別不確定的時候,就可以定義泛型類。 有了泛型類,省去了曾經的強轉和型別轉換異常的麻煩。 

部分程式碼:

class Mygen<E>{
	public void add(E e){
		System.out.println(e.toString());
	}
	
	public Object out(Object obj){//方法不帶泛型,當返回時候需要強轉,不是很安全
		return obj;
	}
	
	public <A> A myout(A a){//方法帶泛型,但要求和類的泛型相互獨立。可以限定返回型別和方法的實參相同,更安全,而且不用強轉。
		System.out.println("myout:" +a);
		return a;
	}
	
	
	//	//靜態方法帶泛型,泛型一定要獨立於類,因為它沒有物件。
	public static <E> E myout1(E a){
		System.out.println("myout:" +a);
		return a;
	}
	
	


完整簡單程式碼演示《細節都在程式碼解釋中》:

package cn.hncu.generic;

public class genericdemo2 {

	
	public static void main(String[] args) {
		Mygen<String > my =new Mygen<String>();
		my.add("abc");
		//my.add(21);//編譯錯誤,泛型規定只能使用String型別的
		String s=(String) my.out("asd");//方法不帶泛型,返回值需要強轉,不是很安全
		//返回值也必須是泛型型別; 泛型很安全
		
	
		//方法帶泛型,但要求和類的泛型相互獨立。可以限定返回型別和方法的實參相同,更安全,而且不用強轉。
		 int m=my.myout(11);//myout:11;返回值和傳進去的引數型別必須一致;
		 	char c=my.myout1('a');
		
	}

}
class Mygen<E>{
	public void add(E e){
		System.out.println(e.toString());
	}
	
	public Object out(Object obj){//方法不帶泛型,當返回時候需要強轉,不是很安全
		return obj;
	}
	
	public <A> A myout(A a){//方法帶泛型,但要求和類的泛型相互獨立。可以限定返回型別和方法的實參相同,更安全,而且不用強轉。
		System.out.println("myout:" +a);
		return a;
	}
	
	
	//	//靜態方法帶泛型,泛型一定要獨立於類,因為它沒有物件。
	public static <E> E myout1(E a){
		System.out.println("myout:" +a);
		return a;
	}
	
	
	
}
當某些類用的介面泛型時候,我們也能解決,要求返回和類一樣和不一樣的型別的泛型都能解決,詳見線面程式碼:
package cn.hncu.generic;

public class genericdemo2 {

	
	public static void main(String[] args) {
		Mygen<String > my =new Mygen<String>();
		my.add("abc");
		//my.add(21);//編譯錯誤,泛型規定只能使用String型別的
		String s=(String) my.out("asd");//方法不帶泛型,返回值需要強轉,不是很安全
		//返回值也必須是泛型型別; 泛型很安全
		
	
		//方法帶泛型,但要求和類的泛型相互獨立。可以限定返回型別和方法的實參相同,更安全,而且不用強轉。
		 int m=my.myout(11);//myout:11;返回值和傳進去的引數型別必須一致;
		 	char c=my.myout1('a');
		
	}

}
class Mygen<E>{
	public void add(E e){
		System.out.println(e.toString());
	}
	
	public Object out(Object obj){//方法不帶泛型,當返回時候需要強轉,不是很安全
		return obj;
	}
	
	public <A> A myout(A a){//方法帶泛型,但要求和類的泛型相互獨立。可以限定返回型別和方法的實參相同,更安全,而且不用強轉。
		System.out.println("myout:" +a);
		return a;
	}
	
	
	//	//靜態方法帶泛型,泛型一定要獨立於類,因為它沒有物件。
	public static <E> E myout1(E a){
		System.out.println("myout:" +a);
		return a;
	}
	
	
	
}

泛型高階:

泛型的萬用字元:?

當操作的不同容器中的型別都不確定的時候,而且使用的都是元素從Object類中繼承的方法,

這時泛型就用萬用字元?來表示即可。(助理解的比方: 泛型中的多型應用)

當我們的集合中輸出的兩個集合是父子關係,就可以採用萬用字元輸出(最簡單的比喻):程式碼如下

package cn.hncu.generic;

import java.util.ArrayList;
import java.util.Iterator;

public class genericPrint {

	
	public static void main(String[] args) {
			ArrayList<String> list=new ArrayList<String>();
			list.add("qwe");
			list.add("qw1e");
			list.add("qw2e");
			list.add("qw3e");
			
			ArrayList<Integer> list1=new ArrayList<Integer>();
			list1.add(1);
			list1.add(2);
			list1.add(3);
			list1.add(4);
			list1.add(5);
			print(list);
			print(list1);
	}

	private static void print(ArrayList<?> list) {//
		Iterator<?> it =list.iterator();
		while(it.hasNext())
		{
			Object obj =it.next();
			System.out.println(obj);
		}
	}
//因為這是執行期的多型,而我們的泛型在編譯期
//	private static void print(ArrayList<Object> list) {
//		Iterator<Object> it=list.iterator();
//		while(it.hasNext()){
//			Object obj =it.next();編譯通不過;
//			System.out.println(obj);
//		}
//		
//	}
	
	

}
還有就是用到上限與下限

 演示上限或者下限的應用體現(以TreeSet容器為例)

TreeSet的構造方法:
  TreeSet(Collection<? extends E> c) 
    TreeSet(Comparator<? super E> comparator) 

一般情況下:
  只要是往容器中新增元素時,使用上限。 ? extends E
    只要是從容器中取出元素時,是用下限。 ? super E

如在如下程式碼中,person為父類,Studentdemo為子類,當要求兩個一起輸出時候,可以採用上限,當要求比較大小採用通用的比較器的進行比較就可以用到下限,因為所有的子類都可以採用服類物件進行比較,具體程式碼如下:

package cn.hncu.generic;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class genericTopanddown {

	public static void main(String[] args) {
//		extendsdemo();//上限演示
		superdemo();
		
	}

	

	private static void extendsdemo() {
		ArrayList<Person> col=new ArrayList<Person>();
		col.add(new Person("aa", 12));
		col.add(new Person("bb", 19));
		col.add(new Person("cc", 14));
		
		/*
		 *  boolean addAll(int index, Collection<? extends E> c) 
          將指定 collection 中的所有元素都插入到列表中的指定位置(可選操作)。 
		 */
		//能夠實現一次性輸出具有繼承關係的物件
		ArrayList<Studentdemo> cols=new ArrayList<Studentdemo>();//泛型的上限,<? extends E>
		cols.add(new Studentdemo("ee", 15));
		cols.add(new Studentdemo("ff", 16));
		cols.add(new Studentdemo("gg", 17));
//		  col.addAll(cols);//這樣寫不安全,我們是已知二者存在的關係下,嚴謹必須在使用一個集合放相同的型別
		//外加排序 用TreeSet
		TreeSet<Person> set=new TreeSet<Person>();//必須實現compare介面
		set.addAll(col);
		set.addAll(cols);
		  Iterator<Person> it=set.iterator();//迭代器遍歷
		  while(it.hasNext()){
			  Person p=it.next();
			  System.out.println(p);
		  }
	}
	
	//下限演示
	private static void superdemo() {
			TreeSet<Studentdemo> set =new TreeSet<Studentdemo>(new sortByName());//做一個比較器,person的子類都能比較
			set.add(new Studentdemo("aja", 11));
			set.add(new Studentdemo("aja1", 12));
			set.add(new Studentdemo("aja3", 1));
			set.add(new Studentdemo("aja22", 10));
			Iterator<Studentdemo> it =set.iterator();
			while(it.hasNext()){
				System.out.println(it.next());
			}
			//只要是person 的子類或是自己類,都可以通過(new sortByName())進行比較。這就是下限   ? super E

			
	}

}



class Person implements Comparable<Person>{
	String name;
	int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	
	
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}



	public int getAge() {
		return age;
	}



	public void setAge(int age) {
		this.age = age;
	}



	@Override
	public int compareTo(Person o) {
		
		return this.age-o.age;
	}
	
}
class Studentdemo extends Person{
	String name;
	int age;
	public Studentdemo(String name,int age){
		super(name,age);
	}
}

//TreeSet中的按名字比較的比較器(persom)通用。
class sortByName implements Comparator<Person>{

	
	public int compare(Person o1, Person o2) {
		return o1.toString().compareTo(o2.toString());
	}
	
}



相關文章