第27条:优先考虑泛型方法
就如类可以从泛型中受益一般,方法也一样。静态工具方法尤其适合于泛型化。Collections中所有“算法”方法是泛型化的。
publlic static Set union(Set s1, Set s2) {
Set result = new HashSet(s1);
result.addAll(s2);
return result;
}
这个方法会出现两条警告,因为使用的是原生态类型。为了修正这些警告,使方法变成类型安全的,我们这里就可以用到泛型:
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
Set<E> result = new HashSet<E>(s1);
result.addAll(s2);
return result;
}
至少对于简单的泛型方法而言,就是这么回事了。现在改方法编译时不会产生任何警告,并提供了类型安全性,也更容易使用。以下是一个执行该方法的简单程序。程序不包含装换,编译时不会有错误或者警告:
public static void main(String[] args){
Set<String> guys =new HashSet<String>{
Array.asList("Tom","Dick","Harry"));
Set<String> stooges =new
HashSet<String>{
Array.asList("Larry","Moe","Curly"));
Set<String> aflCio=unioc(guys,stooges);
System.out.printle(aflCio);
}
}
运行这段程序是,会打印出[Moe,Harry,Tom,Curly,Larry,Dick]。 元素的顺序是依赖于实现的。
union方法局限在于,三个集合的类型(两个输入参数及一个返回值)必须全部相同。利用有限制的通配符类型可以使这个方法变得更回灵活。
泛型方法的一个显著特征是,无需明确指定类型参数的值,不像调用泛型构造器的时候是必须指定的。对于上述程序而言,编译器发现uniond的两个参数都是Set< String>类型,因此知道类型参数E必须为String,这个过程称作为类型推导(type inference)。
如第一条所述,可以利用泛型方法调用所提供的类型推导,是创建参数化类型实例的过程变得更加轻松。提醒一下:在调用泛型构造器的时候,要明确传递类型参数的值可能有点麻烦。类型参数出现在了变量的声明的左右两边,显得有些冗余:
Map<String, List<String>> anagrams = new HashMap<String, List<String>>();
为了消除冗余,可以编写一个泛型静态工厂方法,与想要使用的每个构造器相对应。例如,下面是一个无参的HashMap构造器相对应的泛型静态工厂方法:
public static <K, V> HashMap<K, V> newHashMap() {
return new HashMap<K, V>();
}
Map<String,List<String>> anagrans=newHashMap();
相关的模式泛型单例工厂。有时,会需要创建不可变但又适合于许多不同类型的对象。 由于泛型是通过擦除来实现的,可以给所有的必要的类型参数使用同一个单个对象,但是需要编写一个静态的工厂方法,重复地给每个必要的类型参数分发对象。这种模式叫做“泛型单例工厂”,这种模式最常用于函数对象。 如Collections.reverseOrder,但也适用于像Collections.emptySet这样的集合。
总而言之,泛型方法就像泛型一样,使用起来比要求客户端转换输入参数并返回值的方法来的更加安全,也更加容易。就像类型一样,你应该确保新的方法可以不用转换就能使用,这通常意味着要将它们泛型化。并且就像类型一样,还应该将现有的方法泛型化,使新用户使用起来更加轻松,且不会破坏现有的客户端。