引言
泛型不僅可以應(yīng)用于整個(gè)類上,還可以在類中包含參數(shù)化的方法贮匕。而這個(gè)類所在的類可以是泛型類,也可以是非泛型類花枫。這就意味著刻盐,是否擁有泛型方法,與所在的類是否是泛型沒有關(guān)系乌昔。
泛型方法可以讓該方法獨(dú)立于整個(gè)類而變化隙疚。這就意味著,只要能做到磕道,那么就應(yīng)當(dāng)使用泛型方法。也就是說行冰,如果泛型方法可以取代整個(gè)類泛型化溺蕉,那么就應(yīng)該只使用泛型方法伶丐,因?yàn)樗梢允故虑楦拥厍宄靼住?br> 另外,對于一個(gè)
static
的方法而言疯特,無法訪問泛型類的類型參數(shù)哗魂,所以static
要想使用泛型,則必須聲明為泛型方法漓雅。
定義
要定義泛型方法录别,只需將泛型參數(shù)列表置于返回值之前,如下邻吞。
public class GenericMethods {
public <T> void f(T x) {
System.out.println(x.getClass().getName());
}
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
gm.f("");
gm.f(1);
gm.f(1.0);
gm.f(1.0f);
gm.f('c');
gm.f(gm);
}
}
// Outputs
java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Float
java.lang.Character
com.daidaijie.generices.method.GenericMethods
GenericMethods
并不是參數(shù)化的组题,盡管這個(gè)類和其內(nèi)部的方法可以被同時(shí)參數(shù)化,但是在上面例子中抱冷,只有方法f()
具有類型參數(shù)崔列,這是由該方法的返回類型前面的類型參數(shù)列表指明的。
注意旺遮,當(dāng)使用泛型類的時(shí)赵讯,必須在創(chuàng)建對象的時(shí)候指定類型參數(shù)的值,而使用泛型方法的時(shí)候耿眉,通常不必指明參數(shù)類型边翼,因?yàn)榫幾g器會(huì)為我們找出具體的類型。這稱為類型參數(shù)推斷鸣剪。因此讯私,我們可以像調(diào)用普通方法一樣調(diào)用
f()
,而且就像是f()
被無限次的重載過西傀。它甚至可以接受GenericMethods
作為其類型參數(shù)斤寇。
如果調(diào)用f()
的時(shí)候傳入基本類型,那么將觸發(fā)自動(dòng)打包機(jī)制拥褂,將基本類型的值包裝對應(yīng)的對象娘锁。泛型方法和自動(dòng)打包機(jī)制減少了許多之前我們不得不編寫的代碼。
類型推斷的作用
在Java SE7之前饺鹃,如果要?jiǎng)?chuàng)建一個(gè)泛型對象莫秆,那么將必須寫非常啰嗦的代碼來實(shí)現(xiàn)。如果創(chuàng)建一個(gè)持有List
的Map
悔详,就要像下面那樣镊屎。
Map<Person,List<? extends Pet>> petPeople = new HashMap<Person,List<? extends Pet>>();
然而,在泛型方法中茄螃,類型參數(shù)推斷可以為我們簡化一部分的操作缝驳。我們可以編寫一個(gè)工具類,它包含各種各樣的static
方法,專門用于創(chuàng)建各種常用的容器對象用狱。
public class New {
public static <K, V> Map<K, V> mapOf() {
return new HashMap<K, V>();
}
public static <T> List<T> listOf() {
return new ArrayList<T>();
}
public static <T> LinkedList<T> linkListOf() {
return new LinkedList<T>();
}
public static <T> Set<T> setOf() {
return new HashSet<T>();
}
public static <T> Queue<T> queue() {
return new LinkedList<T>();
}
public static void main(String[] args) {
Map<String, List<String>> sls = New.mapOf();
List<String> ls = New.listOf();
LinkedList<String> lls = New.linkListOf();
Set<String> ss = New.setOf();
Queue<String> qs = New.queue();
}
}
以上的類型推斷的一個(gè)使用运怖,盡管說這段代碼在Java SE7之前為創(chuàng)建類型少寫了很多代碼,但是如果有人需要閱讀以上的代碼夏伊,他必須去分析工具類New
摇展,以及New
所隱含的功能,這似乎導(dǎo)致了與不使用News
的工作效率差不多溺忧。
類型推斷的限制
在Java SE8之前咏连,類型推斷也有一定限制,類型推斷只對賦值操作有效鲁森,將一個(gè)泛型方法調(diào)用的結(jié)果作為參數(shù)祟滴,傳遞給另一個(gè)方法,這時(shí)候編譯器并不會(huì)執(zhí)行類型判斷刀森。在這種情況下踱启,編譯器認(rèn)為,調(diào)用泛型方法后研底,其返回值被賦給一個(gè)Object
類型的變量埠偿。
public class LimitsOfInference {
static void f(Map<Person, List<? extends Pet>> petPeople) {}
public static void main(String[] args) {
f(New.mapOf()); // compile error before java8
}
}
顯式的類型說明
在泛型的方法中,可以顯示的指明類型榜晦,不過這種語法非常少用冠蒋。要顯示的指明類型,必須在點(diǎn)操作符與方法名之間插入尖括號(hào)乾胶,然后將類型置于尖括號(hào)內(nèi)抖剿。如果是定義該方法的類的內(nèi)部,必須在點(diǎn)操作符之前使用this
關(guān)鍵字识窿,如果是使用static
的方法斩郎,必須在點(diǎn)操作符之前加上類名,這種寫法看解決LimitsOfInference
中的問題喻频。
public class LimitsOfInference {
static void f(Map<Person, List<Pet>> petPeople) {
}
public static void main(String[] args) {
f(New.<Person, List<Pet>>mapOf());
}
}
泛型和可變參數(shù)列表也能夠很好的共存
public class GenericVarargs {
public static <T> List<T> makeList(T... args) {
List<T> result = new ArrayList<>();
for (T item : args) {
result.add(item);
}
return result;
}
public static void main(String[] args) {
List<String> ls = makeList("A");
System.out.println("ls = " + ls);
ls = makeList("A", "B", "C");
System.out.println("ls = " + ls);
ls = makeList("ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""));
System.out.println("ls = " + ls);
}
}
// Outputs
ls = [A]
ls = [A, B, C]
ls = [A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]