2016-07-11
未使用泛型舉例:

說明:任何Object子類均可以存入Object集合,但是從集合中獲取元素,則是Object對象,需要強制轉換,可能拋異常。
使用泛型能夠限定集合中存放的元素類型,標記集合中的元素類型,對存入集合的元素進行類型檢查,讓集合中的元素類型一致。泛型就是“參數化類型”,類比於形參和實參的關系,泛型就是讓原本具體類型進行參數化,在泛型具體化或者泛型調用的時候,將類型實參傳給類型形參,例如String這個實參傳給T形參。
public void test1(){
//未使用泛型
List list=new ArrayList();
list.add(1);
//加入類型不一致
list.add("one");
for(int i=0;i<list.size();i++){
//可能出現ClassCastException
int num=(int) list.get(i);
}
}
public void test2(){
//使用泛型,保證了集合使用的安全性
List<Integer> list=new ArrayList<>();
list.add(1);
//不能加入其它類型
// list.add("one");
for(int i=0;i<list.size();i++){
//不會出現ClassCastException
int num=list.get(i);
}
}
泛型類及方法的使用
class Animal<T>{
}
//Cat繼承Animal
//指定T為Short,Cat為具體類,不是泛型類
class Cat extends Animal<Short>{
}
//泛型類Persion繼承Animal
//還未指定T,Persion是泛型類
class Persion<T> extends Animal<T>{
private T t;
List<T> list=new ArrayList<>();
public T getT(){
return this.t;
}
public void setT(T t){
this.t=t;
}
//泛型方法
public <E> E change(E e){
return e;
}
}
public class Generic_Use{
public static void main(String[] args) {
//泛型類使用
{
//在實例化泛型類時,未指定泛型的具體類型,則泛型的默認類型為Object
Persion persion=new Persion();
persion.setT("helloWorld");
//persion.getT() 返回Object類型 需要強制轉換為String
String str=(String) persion.getT();
}
{
//在實例化泛型類時,指定了泛型的具體類型A,則所有出現泛型的地方均為類型A
Persion<String> persion=new Persion<>();
persion.setT("helloWorld");
//persion.getT() 返回String類型
System.out.println(persion.getT());
{
//泛型方法的具體化
//persion.change(11)均返回Integer類型
System.out.println(persion.change(11));
//通用的泛型方法具體化
System.out.println(persion.<Integer>change(11));
//出錯 無法指定類泛型具體類型為Integer的方法又用String進行 具體化
// System.out.println(persion.<Integer>change("12"));
}
}
}
泛型接口的使用類似泛型類,可類比使用
public class Generic_extends {
public static void main(String[] args) {
{
//普通類與繼承
Object object=null;
String str="str";
object=str;
Object[] objects=null;
String[] strs=new String[]{"str1","str2"};
objects=strs;
}
{
//泛型類與繼承
List<Object> list=null;
List<String> list2=null;
//出錯 List<String> 不是List<Object>的父類
// list=list2;
{
//證明
//反證:若List<Object>是 List<String>的父類
list.add(11);
String str=list2.get(0);//會出錯
//結論:若A是B的父類,但是List<A>不是List<B>的父類
}
}
}
}
原則:對於集合的使用,則是嚴進寬出,即放入集合要類型准確一致,取出可以模糊取出
注意點:靜態方法和catch塊不能使用泛型
建議:參考C++中的模板編程思想
public void test3(){
//通配符?
{
//List<?>是所有List<對象> 的父類
//List<?>可以指向任意的List<對象>實例對象,並取出其中的元素,但是不能放入入元素
List<?> list=new ArrayList<>();
List<Object> list2=new ArrayList<>();
List<String> list3=new ArrayList<>();
list2.add(12);
list3.add("bbb");
list=list2;
list=list3;
{
//list取出存在list3的元素
Iterator<?> iterator=list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//list不能直接通過自己向他的子類放入元素
//list向list3中存入元素
// list.add("ccc");//出錯
//特例 空對象
list.add(null);
}
}
{
//邊界通配符 ? extends A
//允許集合中的元素類型是A及其子類(類型通配符上限 類比小於號《)
List<? extends Number> list4=new ArrayList<>();
List<Integer> list5=new ArrayList<>();
list5.add(12);
list4=list5;
//list4取出存在list5中的元素
System.out.println(list4.get(0));
//list4向list5中存入元素
// list4.add(12);//出錯
}
{
//邊界通配符 ? super A
//允許集合中的元素類型是A及其父類(類型通配符下限 類比大於號》)
List<? super Integer> list6=new ArrayList<>();
List<Integer> list7=new ArrayList<>();
list7.add(12);
list6=list7;
//list6取出存在list7中的元素
System.out.println(list6.get(0));
//list6向list7中存入元素
// list6.add(12);//出錯
// list7=list6;//出錯
}
//結論:
//對於泛型,父類可以從子類中取元素,但是不能存元素到子類中
//或者說可以從通配符泛型類中取出元素,但是不能寫入元素
//體現了,集合中的元素嚴進寬出,寫入集合的元素需要具體類型,而讀取集合的元素沒有要求
}
}
public void test4(){
List<Integer> list=new ArrayList<>();
List<String> list2=new ArrayList<>();
System.out.println("list的類型="+list.getClass());
System.out.println("list2的類型="+list2.getClass());
//結果:
//list的類型=class java.util.ArrayList
//list2的類型=class java.util.ArrayList
}
從上面程序可以看出方法test4中list和list2的實際運行類型是一致的。
這關系到泛型的類型擦除。
原因分析:泛型設計的初衷就是在程序編寫的時候就能夠進行類型檢查,避免程序運行時出錯。對於泛型類型檢查通過後,所以在編譯時,會將泛型的相關信息擦出,也就是說,成功編譯過後的class文件中是不包含任何泛型信息的。其實就是好比C++中模板編程,所有同一個泛型的具體化對象實際均是同一類型,所有同一泛型的具體化對象其實是共享同一份代碼,即母代碼,可以類比java中類和實例之間的關系學習。