博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Effective Java学习(泛型)之——优先考虑泛型化方法
阅读量:4152 次
发布时间:2019-05-25

本文共 4508 字,大约阅读时间需要 15 分钟。

       就如类可以从泛型中受益一般,方法也是一样。静态工具方法尤其适合于泛型化。Collections中的所有的“算法”方法,例如(binarySearch和sort)都泛型化了。

 

编写泛型化的方法与编写泛型化的类相似。例如下面这个方法,他返回两个集合的联合:

 

public static Set union(Set set1,Set set2){		Set result = new HashSet(set1);		result.addAll(set2);		return result;	}

 此时,这个方法编译时就会出现两个警告:

 

 

       为了修复这些警告,使方法变成是类型安全的,要将方法声明修改为声明一个类型参数,表示这三个集合的元素类型,并在方法中使用类型参数,声明类型参数的类型参数列表,处在方法的修饰符及返回值之间,在这个示例中,类型参数列表为<E>,返回类型为Set<E>。类型参数的命名惯例与泛型方法以及泛型的相同:

 

public static 
Set
union(Set
set1,Set
set2){ Set
result = new HashSet
(set1); result.addAll(set2); return result; }

       以上至少对于简单的泛型方法而言,就这么回事了,现在方法编译时不会产生任何警告,并提供了类型转换安全性,也更容易使用,以下是一个执行该方法的简单程序,程序中不包含转换,编译时不会有错误或者警告:

 

 

public static void main(String[] args) {		Set
guys = new HashSet
( Arrays.asList("Tom", "Dick","Harry")); Set
stooges = new HashSet
( Arrays.asList("Larry", "Moe","Curly")); Set
aflCio = union(guys, stooges); System.out.println(aflCio); }

 结果为:[Moe, Harry, Tom, Curly, Larry, Dick],元素的顺序是依赖于实现的。

 

 

      union方法的局限性在于,三个集合的类型必须全部相同,利用有限制的通配符,可以是这个方法便得更加灵活。

 

      泛型方法的一个显著特性是,无需明确指定类型的参数值,不像调用泛型构造器的时候是必须指定的,编译器通过检查方法参数的类型来计算类型参数的值,对于上述的程序而言,编译器发现union两个参数都是Set<String>类型,因此知道类型参数<E>必须是String类型,这个过程称为类型推导(type inference)。

 

       可以利用泛型方法调用所提供的类型推导,是创建参数化类型实例的过程便得更加轻松,提醒一下:在调用泛型构造器的时候,要明确传递类型参数的值可能有点麻烦。类型参数出现在了变量声明的左右两边,显得有些冗余:

 

Map
> anagrams = new HashMap
>();

       为了消除这种冗余,可以编写一个泛型工厂方法,与想要使用的每个构造器相对应,例如,下面是一个与无参的HashMap构造器相对应的泛型静态工厂方法:

 

 

public static 
HashMap
newHashMap(){ return new HashMap
();}

 

 

通过这种工厂方法,可以用下面这段简洁的代码来取代上面那个重复的声明

 

Map
> anagrams = newHashMap();

        在泛型上调用构造器时,如果语言所做的类型推导与调用泛型方法时所做的相同,那就好了,将来的某一天也许可以实现这一点,但截至到java1.6版本发行还是不行。

 

 

      相关的模式是泛型单例工厂(generic singleton factory)。有时候,会需要创建不可变但又适合于许多不同类型的对象,由于泛型是通用擦除实现的,可以给所有必要饿类型参数使用单个对象,但是需要编写一个静态工厂方法,重复的给每个必要的类型参数分发对象,这种模式是最常用于函数对象,如Collentions,reverseOrder,但也适用于像Collections,emptySet这样的集合。

 

假设有一个接口,描述了一个方法,该方法接受和返回某个类型T的值:

 

public interface UnaryFunction
{ T apply(T args);}

      现在假设要提供一个恒等函数。如果每次需要的时候都重新创建一个,这样会很浪费,因为他是无状态的,如果泛型被具体化了,每个类型都需要一个恒等函数,但是他们被擦除以后,就只需要一个泛型单例,请看下面示例:

 

 

private static UnaryFunction IDENTITY_FUNCTION = new UnaryFunction() {		@Override		public Object apply(Object args) {			return args;	}};		@SuppressWarnings("unchecked")public static 
UnaryFunction
indetityFunction(){ return (UnaryFunction
)IDENTITY_FUNCTION;}

 

 

      IDENTITY_FUNCTION转换成(UnaryFunction<T>),产生了一条未受检的转换警告,因为UnaryFunction<Object>对于每一个T来说并非都是UnaryFunction<T>。但恒等函数很特殊,他返回未被修改的参数,因此我们知道无论T的值是什么?用它作为UnaryFunction<T>都是类型安全的,因此,我们可以放心的禁止有这个转化所产生的未受检转换警告,一旦禁止,代码在编译时就不会出现任何警告或错误。

 

      以下是一个范例程序,利用泛型单例作为UnaryFunction<String>和UnaryFunction<Number>。像往常一样,他不包含转换,编译时没有出现错误或者警告:

 

public static void main(String[] args) {			String[] strings = {"jute","hemp","nylon"};			UnaryFunction
sameString = indetityFunction(); for (String string : strings) { System.out.print(sameString.apply(string)+" "); } System.out.println(); Number[] numbers = {1,2.0,3L}; UnaryFunction
sameNumber = indetityFunction(); for (Number number : numbers) { System.out.print(sameNumber.apply(number)+" "); } }

       虽然相对很少见,但是通过某个包含该类型参数本事表达式来限制类型参数是允许的,这就是递归类型限制,递归类型限制最普遍的用途就是与Comparable接口有关,他定义类型的自然顺序:

 

 

public interface Comparable
{ int compareTo(T o);}

       类型参数T定义的类型,可以与实现Comparable<T> 的类型元素进行比较,实际上,几乎所有的的类型只能与他们自身的类型的元素相比较。因此,例如String实现Comparable<String> Integer实现了Comparable<Integer>等等.

 

 

       有许多方法都带有一个实现Comparable接口的元素列表,为了对列表进行排序,并在其中进行搜索,计算出他的最小值或者最大值,等等。要完成者其中的任何一项工作,要求列表中的每个元素要都能与列表中的每一个元素相比较,换句话说,列表的元素可以互相比较,下面是如何表达这种约束条件的一个示例:

 

public static 
> T max(List
list){...}

      类型限制<T extends Comparable<T>> 可以读作“针对可以自身进行比较的每个类型T”。这与互比性的概念或多或少有些一致。

 

下面的方法就是上述声明。他根据元素自然顺序计算列表的最大值,编译时没有出现错误和警告:

 

public static 
> T max(List
list){ Iterator
iter = list.iterator(); T result = iter.next(); while(iter.hasNext()){ T t = iter.next(); if (t.compareTo(result) > 0) { result = t; } } return result; }

       递归类型限制可能比这个复杂的很多,蛋幸运的是,这种情况并不经常发生。如果你理解了这种习惯用法及其通配符变量,就能够处理在实践中,遇到的许多递归类型限制了。

 

 

       总而言之,泛型方法就像泛型一样,使用起来比要求客户端转换输入参数并返回值的方法来的更加安全,也更加容易,就像泛型一样,你应该确保新方法可以不用转换就能够使用,这通常意味着要将他们泛型化,并且就像类型一样,还应该将现有的方法泛型化,使新用户使用起来更加轻松,且不会破坏现有的客户端。

 

转载地址:http://milti.baihongyu.com/

你可能感兴趣的文章
九度:题目1027:欧拉回路
查看>>
九度:题目1012:畅通工程
查看>>
九度:题目1017:还是畅通工程
查看>>
九度:题目1034:寻找大富翁
查看>>
第六章 背包问题——01背包
查看>>
51nod 分类
查看>>
1136 . 欧拉函数
查看>>
面试题:强制类型转换
查看>>
Decorator模式
查看>>
Template模式
查看>>
Observer模式
查看>>
高性能服务器设计
查看>>
性能扩展问题要趁早
查看>>
MySQL-数据库、数据表结构操作(SQL)
查看>>
OpenLDAP for Windows 安装手册(2.4.26版)
查看>>
图文介绍openLDAP在windows上的安装配置
查看>>
Pentaho BI开源报表系统
查看>>
Pentaho 开发: 在eclipse中构建Pentaho BI Server工程
查看>>
JSP的内置对象及方法
查看>>
android中SharedPreferences的简单例子
查看>>