黑馬程序員--java高新技術--java5的泛型

---------------------- android培訓java培訓、期待與您交流! ----------------------

================7單元:java5的泛型===================

36.入門泛型的基本應用

37.泛型的內部原理及更深應用

38.泛型的通配符擴展應用

39.泛型集合的綜合應用案例

40.自定義泛型方法及其應用

41.自定義泛型方法的練習與類型推斷總結

42.自定義泛型類的應用

43.通過反射獲得泛型的實際類型參數


********************************************************************************************************************

================7單元:java5的泛型===================

36.入門泛型的基本應用

l Jdk 1.5以前的集合類中存在什麼問題

ArrayList collection = new ArrayList();

collection.add(1);

collection.add(1L);

collection.add("abc");

int i = (Integer) collection.get(1);//編譯要強制類型轉換且運行時出錯!

l Jdk 1.5的集合類希望你在定義集合時,明確表示你要向集合中裝哪種類型的數據,無法加入指定類型以外的數據

ArrayList<Integer> collection2 = new ArrayList<Integer>();

collection2.add(1);

/*collection2.add(1L);

collection2.add(“abc”);*///這兩行代碼編譯時就報告了語法錯誤

int i2 = collection2.get(0);//不需要再進行類型轉換

l 泛型是提供給javac編譯器使用的,可以限定集合中的輸入類型,讓編譯器擋住源程序中的非法輸入,編譯器編譯帶類型說明的集合時會去除掉類型信息,使程序運行效率不受影響,對於參數化的泛型類型,getClass()方法的返回值和原始類型完全一樣。由於編譯生成的字節碼會去掉泛型的類型信息,只要能跳過編譯器,就可以往某個泛型集合中加入其它類型的數據,例如,用反射得到集合,再調用其add方法即可。

37.泛型的內部原理及更深應用

l ArrayList<E>類定義和ArrayList<Integer>類引用中涉及如下術語:

整個稱爲ArrayList<E>泛型類型

ArrayList<E>中的E稱爲類型變量或類型參數

整個ArrayList<Integer>稱爲參數化的類型

ArrayList<Integer>中的Integer稱爲類型參數的實例或實際類型參數

ArrayList<Integer>中的<>念着typeof

ArrayList稱爲原始類型

l 參數化類型與原始類型的兼容性:

參數化類型可以引用一個原始類型的對象,編譯報告警告,例如,
Collection<String> c = new Vector();//可不可以,不就是編譯器一句話的事嗎?

原始類型可以引用一個參數化類型的對象,編譯報告警告,例如,
Collection c = new Vector<String>();//原來的方法接受一個集合參數,新的類型也要能傳進去

l 參數化類型不考慮類型參數的繼承關係:

Vector<String> v = new Vector<Object>(); //錯誤!///不寫<Object>沒錯,寫了就是明知故犯

Vector<Object> v = new Vector<String>(); //也錯誤!

l 編譯器不允許創建泛型變量的數組。即在創建數組實例時,數組的元素不能使用參數化的類型,例如,下面語句有錯誤:

  Vector<Integer> vectorList[] = new Vector<Integer>[10];

l 思考題:下面的代碼會報錯誤嗎?

Vector v1 = new Vector<String>(); 

Vector<Object> v = v1;

38.泛型的通配符擴展應用

l 問題:

定義一個方法,該方法用於打印出任意參數化類型的集合中的所有數據,該方法如何定義呢?

l 錯誤方式:

public static void printCollection(Collection<Object> cols) {

for(Object obj:cols) {

System.out.println(obj);

}

/* cols.add("string");//沒錯

 cols = new HashSet<Date>();//會報告錯誤!*/

}

l 正確方式:

public static void printCollection(Collection<?> cols) {

for(Object obj:cols) {

System.out.println(obj);

}

//cols.add("string");//錯誤,因爲它不知自己未來匹配就一定是String

cols.size();//沒錯,此方法與類型參數沒有關係

 cols = new HashSet<Date>();

}

l 總結:

使用?通配符可以引用其他各種參數化的類型,?通配符定義的變量主要用作引用,可以調用與參數化無關的方法,不能調用與參數化有關的方法。

l 限定通配符的上邊界:

正確:Vector<? extends Number> x = new Vector<Integer>();

錯誤:Vector<? extends Number> x = new Vector<String>();

l 限定通配符的下邊界:

正確:Vector<? super Integer> x = new Vector<Number>();

錯誤:Vector<? super Integer> x = new Vector<Byte>();

l 提示:

限定通配符總是包括自己。

?只能用作引用,不能用它去給其他變量賦值

Vector<? extends Number> y = new Vector<Integer>();

Vector<Number> x = y;

上面的代碼錯誤,原理與Vector<Object > x11 = new Vector<String>();相似,

只能通過強制類型轉換方式來賦值。

39.泛型集合的綜合應用案例

能寫出下面的代碼即代表掌握了Java的泛型集合類:

 HashMap<String,Integer> hm = new HashMap<String,Integer>();

  hm.put("zxx",19);

  hm.put("lis",18);

  

  Set<Map.Entry<String,Integer>> mes= hm.entrySet();

  for(Map.Entry<String,Integer> me : mes) {

   System.out.println(me.getKey() + ":" + me.getValue());

  }

對在jsp頁面中也經常要對SetMap集合進行迭代:

<c:forEach items=“${map}” var=“entry”>

${entry.key}:${entry.value}

</c:forEach>

40.自定義泛型方法及其應用

如下函數的結構很相似,僅類型不同:

int add(int x,int y) {

return x+y;

 }

float add(float x,float y) {

return x+y;

}

double add(double x,double y) {

return x+y;

}

C++用模板函數解決,只寫一個通用的方法,它可以適應各種類型,示意代碼如下:

template<class T> 

T add(T x,T y) {

return (T) (x+y);

}

Java的泛型方法沒有C++模板函數功能強大,java中的如下代碼無法通過編譯:

<T> T add(T x,T y) {

return (T) (x+y);

//return null;

}

用於放置泛型的類型參數的尖括號應出現在方法的其他所有修飾符之後和在方法的返回類型之前,也就是緊鄰返回值之前。按照慣例,類型參數通常用單個大寫字母表示。

交換數組中的兩個元素的位置的泛型方法語法定義如下:

static <E> void swap(E[] a, int i, int j) {

E t = a[i];

a[i] = a[j];

a[j] = t;

}//或用一個面試題講:把一個數組中的元素的順序顛倒一下

只有引用類型才能作爲泛型方法的實際參數,swap(new int[3],3,5);語句會報告編譯錯誤。

除了在應用泛型時可以使用extends限定符,在定義泛型時也可以使用extends限定符,例如,Class.getAnnotation()方法的定義。並且可以用&來指定多個邊界,如<V extends Serializable & cloneable> void method(){}

普通方法、構造方法和靜態方法中都可以使用泛型。

也可以用類型變量表示異常,稱爲參數化的異常,可以用於方法的throws列表中,但是不能用於catch子句中。

在泛型中可以同時有多個類型參數,在定義它們的尖括號中用逗號分,例如:

public static <K,V> V getValue(K key) { return map.get(key);}

41.自定義泛型方法的練習與類型推斷總結

編譯器判斷範型方法的實際類型參數的過程稱爲類型推斷,類型推斷是相對於知覺推斷的,其實現方法是一種非常複雜的過程。

根據調用泛型方法時實際傳遞的參數類型或返回值的類型來推斷,具體規則如下:

當某個類型變量只在整個參數列表中的所有參數和返回值中的一處被應用了,那麼根據調用方法時該處的實際應用類型來確定,這很容易憑着感覺推斷出來,即直接根據調用方法時傳遞的參數類型或返回值來決定泛型參數的類型,例如:

 swap(new String[3],3,4)   à    static <E> void swap(E[] a, int i, int j)

當某個類型變量在整個參數列表中的所有參數和返回值中的多處被應用了,如果調用方法時這多處的實際應用類型都對應同一種類型來確定,這很容易憑着感覺推斷出來,例如:

 add(3,5)   à static <T> T add(T a, T b) 

當某個類型變量在整個參數列表中的所有參數和返回值中的多處被應用了,如果調用方法時這多處的實際應用類型對應到了不同的類型,且沒有使用返回值,這時候取多個參數中的最大交集類型,例如,下面語句實際對應的類型就是Number了,編譯沒問題,只是運行時出問題:

 fill(new Integer[3],3.5f)   à static <T> void fill(T[] a, T v) 

當某個類型變量在整個參數列表中的所有參數和返回值中的多處被應用了,如果調用方法時這多處的實際應用類型對應到了不同的類型, 並且使用返回值,這時候優先考慮返回值的類型,例如,下面語句實際對應的類型就是Integer了,編譯將報告錯誤,將變量x的類型改爲float,對比eclipse報告的錯誤提示,接着再將變量x類型改爲Number,則沒有了錯誤:

 int x =(3,3.5f)   à static <T> T add(T a, T b) 

參數類型的類型推斷具有傳遞性,下面第一種情況推斷實際參數類型爲Object,編譯沒有問題,而第二種情況則根據參數化的Vector類實例將類型變量直接確定爲String類型,編譯將出現問題:

copy(new Integer[5],new String[5]) à static <T> void copy(T[] a,T[]  b);

copy(new Vector<String>(), new Integer[5]) à static <T> void copy(Collection<T> a , T[] b);

42.自定義泛型類的應用

如果類的實例對象中的多處都要用到同一個泛型參數,即這些地方引用的泛型類型要保持同一個實際類型時,這時候就要採用泛型類型的方式進行定義,也就是類級別的泛型,語法格式如下:

public class GenericDao<T> {

private T field1;

public void save(T obj){}

public T getById(int id){}

}

類級別的泛型是根據引用該類名時指定的類型信息來參數化類型變量的,例如,如下兩種方式都可以:

GenericDao<String> dao = null;

new genericDao<String>();

注意:

在對泛型類型進行參數化時,類型參數的實例必須是引用類型,不能是基本類型。

當一個變量被聲明爲泛型時,只能被實例變量、方法和內部類調用,而不能被靜態變量和靜態方法調用。因爲靜態成員是被所有參數化的類所共享的,所以靜態成員不應該有類級別的類型參數。

問題:類中只有一個方法需要使用泛型,是使用類級別的泛型,還是使用方法級別的泛型?

43.通過反射獲得泛型的實際類型參數

示例代碼:

Class GenericalReflection {

  private Vector<Date> dates = new Vector<Date>();

  public void setDates(Vector<Date> dates) {

    this.dates = dates;

  }

  public static void main(String[] args) {

   Method methodApply = GenericalReflection.class.getDeclaredMethod("applyGeneric", Vector.class);

   ParameterizedType pType = (ParameterizedType)

                    (methodApply .getGenericParameterTypes())[0];

            System.out.println("setDates("

                    + ((Class) pType.getRawType()).getName() + "<"

                    + ((Class) (pType.getActualTypeArguments()[0])).getName()

                    + ">)" );

  }

}

泛型DAO的應用:

public abstract class DaoBaseImpl<T> implements DaoBase<T> {

protected Class<T> clazz;

public DaoBaseImpl() {

Type type = this.getClass().getGenericSuperclass();

ParameterizedType pt = (ParameterizedType) type;

this.clazz = (Class) pt.getActualTypeArguments()[0];

System.out.println("clazz = " + this.clazz);

}

}

public class ArticleDaoImpl extends DaoBaseImpl<Article> implements ArticleDao {

}

package cn.itcast.day2;
import java.lang.reflect.*;
import java.util.*;
import cn.itcast.day1.ReflectPoint;
public class GenericTest {
/**
 * @param args
 * @throws Exception 
 * @throws SecurityException 
 */
public static void main(String[] args) throws SecurityException, Exception {
// TODO Auto-generated method stub
ArrayList collection1 = new ArrayList();
collection1.add(1);
collection1.add(1L);
collection1.add("abc");
//int i = (Integer)collection1.get(1);
ArrayList<String> collection2 = new ArrayList<String>();
//collection2.add(1);
//collection2.add(1L);
collection2.add("abc");
String element = collection2.get(0);
//new String(new StringBuffer("abc"));
Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class);
String str2 = constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str2.charAt(2));
ArrayList<Integer> collection3 = new ArrayList<Integer>();
System.out.println(collection3.getClass() == collection2.getClass());
//collection3.add("abc");
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
System.out.println(collection3.get(0));
printCollection(collection3);
//Class<Number> x = String.class.asSubclass(Number.class);
Class<?> y;
Class<String> x = null;//Class.forName("java.lang.String")
y = x;//對
//x = y;//錯
HashMap<String, Integer> maps = new HashMap<String, Integer>();
maps.put("zxx", 28);
maps.put("flx", 33);
maps.put("lhm", 35);
Set<Map.Entry<String, Integer>> entrySet = maps.entrySet();
for(Map.Entry<String, Integer> entry: entrySet)
{
System.out.println(entry.getKey()+":"+entry.getValue());
}
add(3,5);
Number x1 = add(3.5,3);
Object x2 = add(3,"abc");
swap(new String[]{"abc","xyz","itcast"},1,2);
//swap(new int[])T只能是引用類型,不應該是基本數據類型.new int[]已經是一個對象,不能主動裝箱拆箱.
Object obj = "abc";
String x3 = autoConvert(obj);
copy1(new Vector<String>(),new String[10]);
copy2(new Date[10],new String[10]);//兩個取交集
//copy1(new Vector<Date>(),new String[10]);//T是Date類型,String跟Date對不上.傳播性
GenericDao<ReflectPoint> dao = new GenericDao<ReflectPoint>();
dao.add(new ReflectPoint(3,4));
ReflectPoint r = dao.findById(1);
//Vector<Date> v1 = new Vector<Date>();
//通過反射可以知道Date(集合的泛型參數類型).
Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
Type[] types = applyMethod.getGenericParameterTypes();
ParameterizedType pType = (ParameterizedType)types[0];
System.out.println(pType.getRawType());//原始類型
System.out.println(pType.getActualTypeArguments()[0]);//參數類型
}
private static void printCollection(Collection<?> collection) {
// TODO Auto-generated method stub
//collection.add(1);
System.out.println(collection.size());
for(Object obj : collection)
{
System.out.println(obj);
}
}
public static void applyVector(Vector<Date> v1)
{
}
private static <T> void copy1(Collection<T> dest,T[] src)
{
}
private static <T> void copy2(T[] dest,T[] src)
{
}
//採用自定泛型方法的方式打印出任意參數化類型的集合中的所有內容。
private static <T> void printCollection_2(Collection<T> collection,T obj2)
{
System.out.println(collection.size());
for(Object obj : collection)
{
System.out.println(obj);
}
collection.add(obj2);
}
//定義一個方法,可以將任意類型的數組中的所有元素填充爲相應類型的某個對象。
private static <T> void fillArray(T[] a,T obj)
{
for(int i=0; i<a.length;i++)
{
a[i] = obj;
}
}
//編寫一個泛型方法,自動將Object類型的對象轉換成其他類型。
private static <T> T autoConvert(Object obj)
{
return(T)obj;
}
private static <T> void swap(T[] a,int i,int j)
{
T temp = a[i];
a[i] = a[j];
a[j] = temp;
}
private static <T> T  add(T x,T y)
{
return null;
}
}
package cn.itcast.day2;
import java.util.*;
//dao data access object 數據訪問對象(用於進行數據訪問的)對數據庫進行訪問
//crud 增刪改查.
public class GenericDao<T> {
public <T> void add(T x)
{
}
public  T findById(int id)
{
return null;
}
public void delete(T obj)
{}
public void delete(int id)
{}
public void update(T obj)
{}
public Set<T> findByConditions(String where)
{
return null;
}
public T findByUsername(String name)
{
return null;
}
//public static void update2(T obj)靜態方法不能用泛型
public static <E> void update2(E obj)
{}
}


---------------------- android培訓java培訓、期待與您交流! ----------------------
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章