引言
泛型不仅可以应用于整个类上,还可以在类中包含参数化的方法。而这个类所在的类可以是泛型类,也可以是非泛型类。这就意味着,是否拥有泛型方法,与所在的类是否是泛型没有关系。
泛型方法可以让该方法独立于整个类而变化。这就意味着,只要能做到,那么就应当使用泛型方法。也就是说,如果泛型方法可以取代整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情更加地清楚明白。
另外,对于一个static
的方法而言,无法访问泛型类的类型参数,所以static
要想使用泛型,则必须声明为泛型方法。
定义
要定义泛型方法,只需将泛型参数列表置于返回值之前,如下。
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
并不是参数化的,尽管这个类和其内部的方法可以被同时参数化,但是在上面例子中,只有方法f()
具有类型参数,这是由该方法的返回类型前面的类型参数列表指明的。
注意,当使用泛型类的时,必须在创建对象的时候指定类型参数的值,而使用泛型方法的时候,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为类型参数推断。因此,我们可以像调用普通方法一样调用
f()
,而且就像是f()
被无限次的重载过。它甚至可以接受GenericMethods
作为其类型参数。
如果调用f()
的时候传入基本类型,那么将触发自动打包机制,将基本类型的值包装对应的对象。泛型方法和自动打包机制减少了许多之前我们不得不编写的代码。
类型推断的作用
在Java SE7之前,如果要创建一个泛型对象,那么将必须写非常啰嗦的代码来实现。如果创建一个持有List
的Map
,就要像下面那样。
Map<Person,List<? extends Pet>> petPeople = new HashMap<Person,List<? extends Pet>>();
然而,在泛型方法中,类型参数推断可以为我们简化一部分的操作。我们可以编写一个工具类,它包含各种各样的static
方法,专门用于创建各种常用的容器对象。
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();
}
}
以上的类型推断的一个使用,尽管说这段代码在Java SE7之前为创建类型少写了很多代码,但是如果有人需要阅读以上的代码,他必须去分析工具类New
,以及New
所隐含的功能,这似乎导致了与不使用News
的工作效率差不多。
类型推断的限制
在Java SE8之前,类型推断也有一定限制,类型推断只对赋值操作有效,将一个泛型方法调用的结果作为参数,传递给另一个方法,这时候编译器并不会执行类型判断。在这种情况下,编译器认为,调用泛型方法后,其返回值被赋给一个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
}
}
显式的类型说明
在泛型的方法中,可以显示的指明类型,不过这种语法非常少用。要显示的指明类型,必须在点操作符与方法名之间插入尖括号,然后将类型置于尖括号内。如果是定义该方法的类的内部,必须在点操作符之前使用this
关键字,如果是使用static
的方法,必须在点操作符之前加上类名,这种写法看解决LimitsOfInference
中的问题。
public class LimitsOfInference {
static void f(Map<Person, List<Pet>> petPeople) {
}
public static void main(String[] args) {
f(New.<Person, List<Pet>>mapOf());
}
}
泛型和可变参数列表也能够很好的共存
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]
网友评论