實習筆記(七)java泛型
Java 中對於泛型方法的定義:
public <T> T getT(){
.....相關程式碼;
}
其中我對<T>的理解就是申明只是一個泛型方法的標記,T是返回的型別。
對於泛型類的定義:
public class Type<T>{
....相關程式碼
}
上面寫的是關於泛型方法和泛型類的定義。這裡寫這篇部落格的主要目的是為了記錄<? extends T> 和 <? super T>的理解。
<? extends T>是上界萬用字元。邏輯上來說,一個可以往裡面放動物的容器,也可以往裡面放狗,因為狗是繼承了動物的,所以從
邏輯上來說,一個可以放狗的容器也應該是繼承一個可以放動物的容器的。但是Java中這是不對的。這時候就需要上界萬用字元來解決這個問題。
可以定義一個Content<? extends Animal> content,這個content容器裡面可以放任何Animal包括其子類。這使得容器Content之間也有
了一定的聯絡。但是其中的也有一定的副作用。Content類裡面有個getContent()方法,這個方法是不會影響使用的。因為對java編譯器來說,
裡面所有的東西都有一個相同的父親,那就是Animal。但是如果你想要使用setContent()方法,這時就會出現一個問題,問題是什麼呢?那就
是我只知道里面的型別是Animal以及Animal的派生類,但是無法確定具體的型別,是Dog或者Cat等。這裡的型別沒有具體給明,所以編譯器
在這裡只是給了一個佔位符,所以到後面往t裡面放Dog或者Apple的時候他都不知道是不是和Animal相匹配,所以這是不允許使用set的。下面
方法中的 public void setContent(List<T> content) 如果想可以set T包括T的子類的List的時候,可以這麼定義:
public void setContent(List<?extends T> content)。
<? super T>是下界萬用字元。其作用這好和上面的<? extends T>相反。Content<? super Animal> content 表示的是Animal 以及Animal
基類的容器,但不是Dog基類的容器。裡面的setContent()方法是可以正常使用的。因為它規定了最小粒度的下限,那就是Animal。但是它往外面
getContent()就比較費勁,因為他們公共的基類只有Object。所以取出來的內容只能用Object來裝。
所以這裡總結了一點就是如果一個內容他需要set的內容比較多,那就使用<? super T>。如若使用get的多一點,那就使用<? extends T>。
- 泛型引數Class、Class<?>、Class<? extends xx>的對比
/**
* Created by ypyue on 2017/6/19.
*/
public class Main2 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
System.out.println("1-----------");
Dog dog1 = creatNew1(Dog.class);
System.out.println(dog1.name + " is " + dog1.type+ "\n");
System.out.println("2-----------");
Animal ani1 = creatNew2(Cat.class);
Cat cat1 = (Cat) ani1;
System.out.println(cat1.name + " is " + cat1.type + "\n");
System.out.println("3-----------");
Object obj = creatNew3(Dog.class);
Dog dog2 = (Dog) obj;
System.out.println(dog1.name + " is " + dog2.type + "\n");
System.out.println("4-----------");
Object obj2 = creatNew4(Cat.class);
Cat cat2 = (Cat) obj2;
System.out.println(cat2.name + " is " + cat2.type + "\n");
System.out.println("5-----------");
Object obj3 = creatNew5(new Cat());
Cat cat3 = (Cat) obj3;
System.out.println(cat3.name + " is " + cat3.type + "\n");
}
/**
* 此方法是一個非泛型類中的泛型方法,引數為Class<T>型別,可以傳入任何類,但是Class<T>將引數在函式內部的型別固定為 T類,使用clazz.newInstance(),返回的型別也為固定的 T 型別。
* 如: 傳入Dog.class, 函式中的 T 固定為Dog.class,函式返回的是Dog型別,不需要強制轉換為Dog型別
* (當然,函式返回型別也可以是Object型別,但是沒必要)
*/
public static <T> T creatNew1(Class<T> clazz) throws IllegalAccessException, InstantiationException {
System.out.println(clazz);//列印具體傳入的型別。
return clazz.newInstance();
}
/**
* 此方法引數是Class<? extends Animal>,只能傳入Animal及其子類的型別,函式返回型別只能宣告為Animal或Object型別,
* 如:傳入Cat.class,函式返回一個Animal的子類物件,需將此子類物件強制轉換為具體的子類(即Cat類)。
*/
public static Animal creatNew2(Class<? extends Animal> clazz) throws IllegalAccessException, InstantiationException {
System.out.println(clazz);//列印具體傳入的型別。
return clazz.newInstance();
}
/**
* 此方法引數是Class,可傳入任何類,其返回只能宣告為Object類物件。
* 如:傳入Dog.class, 返回的是Object型別的物件,需強制轉換為Dog型別的物件。
*/
public static Object creatNew3(Class clazz) throws IllegalAccessException, InstantiationException {
System.out.println(clazz);//列印具體傳入的型別。
return clazz.newInstance();
}
/**
* 傳入引數Class<?> 和 上一個方法傳入 Class 的區別還不是很清楚,其返回只能宣告為Object類物件。
* 如:Cat.class, 返回的是Object型別的物件,需強制轉換為Cat型別的物件。
*/
public static Object creatNew4(Class<?> clazz) throws IllegalAccessException, InstantiationException {
System.out.println(clazz);//列印具體傳入的型別。
return clazz.newInstance();
}
/**
* 傳入Object型別的物件,返回也宣告為Object型別。
*/
public static Object creatNew5(Object object) {
System.out.println(object.getClass());//列印具體傳入的型別。
return object;
}
}
abstract class Animal {
public String type = "type = animal ";
}
class Dog extends Animal {
public String name = "name = gou";
}
class Cat extends Animal {
public String name = "name = mao";
}
/**
* 這些方法,傳的引數宣告不同,其返回的宣告也必然要隨之不同。
*/
輸出:
1----------- class Dog name = gou is type = animal
2----------- class Cat name = mao is type = animal
3----------- class Dog name = gou is type = animal
4----------- class Cat name = mao is type = animal
5----------- class Cat name = mao is type = animal