Effective Java读书笔记十五(Java Tips.Day.15)
来源:互联网 发布:java集合框架容器 编辑:程序博客网 时间:2024/06/05 20:15
TIP 40 谨慎设计方法签名
本条目会帮助你设计更好的API
请遵循标准的命名习惯
- 名称应易于理解,而且与同一个包中的其它名称风格一致
- 选择与大众认可的名称相一致,或者自然语言中相同含义的词汇作为名称
- 不要过于追求提供便利的方法
不要过于追求提供便利的方法
- 对于类和接口所支持的每个动作,都提供一个功能齐全的方法。
- 每个方法应该尽其所能,不要设计太多太散的方法,否则难以学习、使用、文档化、测试和维护。
- 当一项操作被经常用到的时候,才考虑为它提供快捷方式——提炼为一个方法。
避免过长的参数列表
不要设计超过4个参数的方法,否则
- 难以使用参数太多的方法,很可能还需要不停的参考文档。
- 如果长参数序列的类型相同,简直就是噩梦。如果不小心弄错顺序,方法可以正常执行,但不会按照程序员的意图正常工作。
如果你遇到了这种需求,请考虑使用以下方法来避免:
- 把方法分解成多个方法,每个方法只需要这些参数的一个子集。如果一个类的构造方法的参数太多,可以考虑减少构造方法的参数数量,其它的参数序列用set方法来设置。
- 创建辅助类,用来保存参数的分组。这些辅助类一般为静态成员类(TIP 22)。例如你正在编写一个表示纸牌游戏的类,你会发现经常要传递一双参数来表示花色和点数。这时就可以考虑设计一个静态成员类——纸牌类,这个纸牌类拥有花色和点数这两个field,然后纸牌游戏类的API以及它的内部表示都可以使用这个纸牌类。
- 结合以上两点,从对象构建和方法调用都采用Builder模式,参考TIP 2。
优先使用接口而不是类来表示参数类型
只要有适当的接口可以用来定义参数,就优先使用接口,而不是实现这个接口的类。这也是我们多次说过的,面向接口编程的设计理念。
- 比如,如果一个方法的参数类型是HashMap,就考虑用Map接口作为参数类型,这样你可以传入HashTable、HashMap、TreeMap、TreeMap的子映射表,或者任何其它实现了Map的类型作为方法的参数。如果使用的参数是类而不是接口,则限制了客户端智能传入特定的实现类型。
- 对于boolean参数,优先使用两个元素的枚举类型。例如,设计一个Thermometer(温度计)类型,它带有一个静态方法:
public static Thermometer create(boolean isFahrenheit)
,参数为true则使用华氏度单位,false则使用摄氏度单位。然而,使用枚举来代替boolean,会使代码更易于阅读和编写:public enum TemperatureScale{ FAHRENHEIT,CELSIUS;}
public static Thermometer create(TemperatureScale temperatureScale)
TIP 41 慎用重载
先来看看这段代码,判断一下运行结果会是怎样:
public class CollectionClassifier { public static String classify(Set<?> set){ return "Set"; } public static String classify(List<?> set){ return "List"; } public static String classify(Collection<?> set){ return "Unkown Collection"; } public static void main(String args[]){ Collection<?>[] collections = { new HashSet<String>(), new LinkedList<BigInteger>(), new HashMap<String ,String>().values() }; for (Collection<?> c :collections) { System.out.println(classify(c)); } }}
显然,这个classify方法有三个重载版本,而作者期望的运行结果是:
期望运行结果:
Set
List
UnKnown Collection
但是很遗憾,三个重载的版本中,只有参数类型为Collection<?>
的版本被调用了:
实际运行结果:
UnKnown Collection
UnKnown Collection
UnKnown Collection
重载方法与覆盖方法不同。
覆盖方法——上转型——多态,这个机制的本质是方法的运行时动态绑定。被覆盖的实例方法,总会选择具体的类型作为参数。
但重载与覆盖不同,不会发生方法的动态绑定行为。实际上具体要调用哪个重载版本,编译期就会被决定,而不会等到运行时。
实际上豆爷在用IDEA敲出以上代码后,在运行之前,编译器已经发出了提醒 :
classify(Set<?> set)
和classify(List<?> set)
is never used.
那么,怎样才能保证合适的、正确的重载呢?
安全而保守的策略是,永远不要编写出两个具有相同参数数目的同名方法。而如果方法的参数列表是一个可变参数,那么永远不要重载它。
如果遵守这些规则,程序员就不会陷入到“我到底应该用哪个重载方法”的懵逼状态。
再来看个重载相关的例子:
public class SetList { public static void main(String args[]) { Set<Integer> set = new TreeSet<>(); List<Integer> list = new ArrayList<>(); for (int i = -3; i < 3; i++){ set.add(i); list.add(i); } System.out.println(set + " " + list); for (int i = 0; i < 3; i++) { set.remove(i); list.remove(i); } System.out.println(set + " " + list); }}
显然,该类的作者期望这样的运行结果:
[-3, -2, -1, 0, 1, 2] [-3, -2, -1, 0, 1, 2]
[-3, -2, -1] [-3, -2, -1]
也就是说,本来期望程序删除set和list中的0,1,2数字
但事与愿违:
[-3, -2, -1, 0, 1, 2] [-3, -2, -1, 0, 1, 2]
[-3, -2, -1] [-2, 0, 2]
set算是正常的,但list没有得到期望的结果。
问题就在于, set.remove(i)调用选择重载方法remove(E), 这里的参数E是 集合<Integer>
的元素类型, 将i从int自动装箱到Integer中,这正好是期待的行为。因此set部分能正常工作。
然而,list.remove(i)调用选择重载方法remove(int i),它从索引位置i去除元素。因此, list的调用过程是这样的:
list.remove(0); //删除了值:-3 ,此时list: [-2, -1, 0, 1, 2]list.remove(1); //删除了值:-1 ,此时list: [-2, 0, 1, 2]list.remove(2); //删除了值:1 ,此时list: [-2, 0, 2]
显然,List重载了 remove(E e)
和 remove(int i)
方法,当它在Java 1.5版本中被泛型化之前,List接口有一个 remove(Object o)
而不是 remove(E e)
, 而相应的参数Object 和 int是根本不同的类型,因此程序员永远不会搞混这两个重载版本。
但自从有了泛型和自动装箱机制后,这两种参数类型就不再根本不同了。换句话说,泛型和自动装箱机制破坏了List接口。幸运的是,Java类库中几乎再没有API受到同样的破坏。
这个例子充分的说明了,自动装箱和泛型成为Java语言的一部分后,谨慎重载显得更加重要了。
总之,一般情况下,对于多个具有相同参数数目的方法来说,应该尽量避免重载。可以使用不同的方法名来达到目的。
对于构造器,则应该避免这样的情形:同一组参数只需经过类型转换,就可以被传递给不同的重载构造器。
否则,程序员会很迷茫,到底应该调用哪个构造器。
如果上面的情形都无法避免,则应当保证:当传递同样的参数时,所有重载方法的行为必须一致!如果不能做到这一点,程序员就很难有效的使用重载方法或构造器的,他们就不能理解它为什么不能正常工作。
- Effective Java读书笔记十五(Java Tips.Day.15)
- Effective Java读书笔记一(Java Tips.Day.1)
- Effective Java读书笔记二(Java Tips.Day.2)
- Effective Java读书笔记五(Java Tips.Day.5)
- Effective Java读书笔记六(Java Tips.Day.6)
- Effective Java读书笔记八(Java Tips.Day.8)
- Effective Java读书笔记九(Java Tips.Day.9)
- Effective Java读书笔记十(Java Tips.Day.10)
- Effective Java读书笔记十一(Java Tips.Day.11)
- Effective Java读书笔记十二(Java Tips.Day.12)
- Effective Java读书笔记十三(Java Tips.Day.13)
- Effective Java读书笔记十四(Java Tips.Day.14)
- Effective Java读书笔记十六(Java Tips.Day.16)
- Effective Java读书笔记十七(Java Tips.Day.17)
- Effective Java读书笔记十九(Java Tips.Day.19)
- Effective Java读书笔记二十(Java Tips.Day.20)
- Effective Java读书笔记二一(Java Tips.Day.21)
- Effective Java读书笔记二二(Java Tips.Day.22)
- 等直杆的纵向振动固有模态
- 利用Highcharts创建SVG图片并转为PNG(前端页面)
- CentOS7 变更默认主机名bogon
- ffmpeg之入门---》编译
- 《大型网站技术架构》网站的高性能架构及优化
- Effective Java读书笔记十五(Java Tips.Day.15)
- java 中访问控制符
- c 等差数列应用arranging coins 多种实现方法
- Material Designer设计(中)
- javabean
- 视频编码器评测系统:VideoCodecRank
- MaterialDesign设计(下)
- fcn——free connect your private network from anywhere
- js中的函数