1:爲什麼要使用Java泛型
看看下面程序例子(不使用泛型):
public static void main(String[] args) {
List list = new ArrayList();
list.add("zhangsan");
list.add("lisi");
list.add(102);//沒有使用泛型,代碼出現黃色警告
for(int i= 0; i < list.size();i++){
//list裝載默認是Object類型,由於第三項裝載Integer類型,所以在取對象時強制轉換報錯。
String name = (String) list.get(i);
System.out.println(name);
}
}
出現如下錯誤:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.java.test.genericity.GenericityMain.main(GenericityMain.java:40)由於list沒有使用泛型,代碼出現黃色警告,list裝載默認是Object類型,由於第三項裝載Integer類型,所以在取對象時強制轉換報錯。
List使用泛型帶有String形參:
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("zhangsan");
list.add("lisi");
// list.add(102); 類型已經限定編譯直接報錯通不過,默認是String類型
for(int i= 0; i < list.size();i++){
String name = list.get(i);
System.out.println(name);
}
}
此時list添加第三項數據直接編譯報錯,類型已經限定編譯直接報錯通不過,默認是String類型
2:Java泛型的作用
1:通過結合代碼繼承封裝,減少Java代碼重複冗餘,使得代碼的結構和架構更合理。
2:消除了強制類型轉換 使得代碼可讀性好,減少了很多出錯的機會。
3:Java語言引入泛型的好處是安全簡單。泛型的好處是在編譯的時候檢查類型安全,並且所有的強制轉換都是自動和隱式的,這樣編譯器就可以在一個高的程度上驗證這個類型。
3:泛型幾種使用類型和繼承關係
- 自定義泛型類
- 自定義泛型接口
- 自定義泛型方法
- 瞭解泛型參數繼承關係
- 自定義泛型類
/*** * @ClassName Car * @Description 定義泛型類 * * ***/ public class Car<M> { private M m; public void openCar(M m){ System.out.println("開車"); }; public M endCar(){ System.out.println("關車"); return m; } }
這裏的M表示泛型形參,由於接收來自外部使用時候傳入的類型實參。那麼對於不同傳入的類型實參,生成的相應對象實例的類型是不是一樣的呢?看如下例子:
Car<Person> pCar = new Car<Person>();
Car<Animal> aCar = new Car<Animal>();
System.out.println(pCar.getClass());//class com.java.test.genericity.Car
System.out.println(aCar.getClass());//class com.java.test.genericity.Car
System.out.println(aCar.getClass()== pCar.getClass());//true
看了以上例子,我們可以發現,在使用泛型類時,雖然傳入了不同的泛型實參,但並沒有真正意義上生成不同的類型,傳入不同泛型實參的泛型類在內存上只有一個,即還是原來的最基本的類型(本實例中爲Car),當然,在邏輯上我們可以理解成多個不同的泛型類型,在於Java中的泛型這一概念提出的目的,導致其只是作用於代碼編譯階段,在編譯過程中,對於正確檢驗泛型結果後,會將泛型的相關信息擦出,也就是說,成功編譯過後的class文件中是不包含任何泛型信息的。泛型信息不會進入到運行時階段。
2:泛型接口,泛型接口和泛型類的使用差不多,其中T表示泛型參數:
public interface Map<T> {
public void load(T t);
}
實現泛型接口類的寫法,傳參形式如下:(在實現類實現泛型接口時,如已將泛型類型傳入實參類型,則所有使用泛型的地方都要替換成傳入的實參類型)
public class BaiduMap implements Map<String>{
@Override
public void load(String t) {
// TODO Auto-generated method stub
}
}
實現泛型接口類,不傳形參寫法如下:(不傳形參,與泛型類的定義相同,在聲明類的同時,需要將泛型的形參也一起加進去)
public class BaiduMap<T> implements Map<T>{
@Override
public void load(T t) {
// TODO Auto-generated method stub
}
}
3:泛型方法使用:(如果當前方法所在的Java類中沒有定義T泛型參數,則在方法的前面聲明泛型形參<T>)
public class BaiduMap implements Map<String>{
@Override
public void load(String t) {
}
//如果當前方法所在的Java類中沒有定義T泛型參數,則在方法的前面聲明泛型形參<T>
public <T> T getMethod(Class<T> czz) throws InstantiationException, IllegalAccessException{
T t = czz.newInstance();
return t;
}
}
區別泛型方法和泛型類方法:
//我們在當前類中已經定義了T類型,下面load含有泛型參數,所以泛型T不是泛型方法
@Override
public void load(T t) {
}
//我們在當前方法中getMethod已經定義了M類型,下面getMethod含有泛型參數,所有getMethod是泛型方法
public <M> M getMethod(Class<M> czz) throws InstantiationException, IllegalAccessException{
M m = czz.newInstance();
return m;
}
4:瞭解泛型的繼承關係以及泛型參數的上下行邊界:
//當前定義了一個類Car 約定了M形參繼承Person類。
public class Car<M extends Person> {
private M m;
public void openCar(M m){
System.out.println("開車");
};
public M endCar(){
System.out.println("關車");
return m;
}
}
public class Person {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
public class Man extends Person{
}
public class Sedan extends Car<Man>{
@Override
public void openCar(Man m) {
// TODO Auto-generated method stub
super.openCar(m);
}
@Override
public Man endCar() {
// TODO Auto-generated method stub
return super.endCar();
}
}
我定義了一個Car類,約束當前的形參M繼承Person,此時我定義一個Man的類作爲形參,繼承了Person,Sedan類繼承Car在Sedan類中我傳遞形參Man,正因爲形參Man,重寫方法openCar,endCar的參數纔會顯示Man參數,這樣就約定了泛型的邊界,使得子類傳遞的參數必須是泛型約定的參數。