第30条:用enum代替int常量
枚举类型对比int枚举,String枚举等
- 易读(不同枚举可以包含同名等)
- 更加安全(不同枚举值不可以互操作)
- 功能更加强大(可以添加任意的方法和域,并实现任意接口,为了将数据与枚举常量关联起来,得声明实例域,并编写一个带有数据并将数据保存在域中的构造器)
可以将数据与枚举常量关联起来,得声明实例域,并编写一个带有数据并将数据保存在域中的构造器
可以将不同行为与每个枚举常量关联起来,特定于常量的类主体
如果多个枚举常量同时共享相同的行为,则考虑策略枚举,而不是switch
枚举中的switch语句适合于给外部的枚举类型增加特定于常量的行为。如针对Operation的每个操作返回相反的操作
// Enum type with constant-specific method implementations
public enum Operation {
PLUS { double apply(double x, double y){return x + y;} },
MINUS { double apply(double x, double y){return x - y;} },
TIMES { double apply(double x, double y){return x * y;} },
DIVIDE { double apply(double x, double y){return x / y;} };
abstract double apply(double x, double y);
}
第31条:用实例域代替序数
永远不要根据枚举的序数导出与它关联的值,而是要将它保存在一个实例域中。
// Abuse of ordinal to derive an associated value - DON'T DO THIS
public enum Ensemble {
SOLO, DUET, TRIO, QUARTET, QUINTET,
SEXTET, SEPTET, OCTET, NONET, DECTET;
public int numberOfMusicians() { return ordinal() + 1; }
}
应该这样用,比较好
public enum Ensemble {
SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),
SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8),
NONET(9), DECTET(10), TRIPLE_QUARTET(12);
private final int numberOfMusicians;
Ensemble(int size) { this.numberOfMusicians = size; }
public int numberOfMusicians() { return numberOfMusicians; }
}
第32条:用EnumSet代替位域
EnumSet可以代替位算法来传递丰富的参数,如下例子用EnumSet来设置Text的style
// EnumSet - a modern replacement for bit fields
public class Text {
public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
// Any Set could be passed in, but EnumSet is clearly best
public void applyStyles(Set<Style> styles) { ... }
}
将EnumSet实力传递给applyStyle这个方法的接口参数
text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
总结:正式因为枚举类型要用在集合(Set)中,所以没有理由用位域来表示它。
第33条:用EnumMap代替序数索引
有时候会看见用ordinal方法来索引数组的代码,如:
public class Herb {
public enum Type { ANNUAL, PERENNIAL, BIENNIAL }
private final String name;
private final Type type;
Herb(String name, Type type) {
this.name = name;
this.type = type;
}
@Override public String toString() {
return name;
}
}
// Using ordinal() to index an array - DON'T DO THIS!
Herb[] garden = ... ;
Set<Herb>[] herbsByType = // Indexed by Herb.Type.ordinal()
(Set<Herb>[]) new Set[Herb.Type.values().length];
for (int i = 0; i < herbsByType.length; i++)
herbsByType[i] = new HashSet<Herb>();
for (Herb h : garden)
herbsByType[h.type.ordinal()].add(h);
// Print the results
for (int i = 0; i < herbsByType.length; i++) {
System.out.printf("%s: %s%n",
Herb.Type.values()[i], herbsByType[i]);
}
这种方法虽然的确可行,但是隐藏这许多问题,数组不知道它的索引代表着什么,你必须手工标注这些索引的输出。但是按照枚举的顺序进行索引数组的时候,int不能提供枚举的类型安全。
这时候需要使用EnumMap,快速实现枚举值映射
// Using an EnumMap to associate data with an enum
Map<Herb.Type, Set<Herb>> herbsByType =
new EnumMap<Herb.Type, Set<Herb>>(Herb.Type.class);
for (Herb.Type t : Herb.Type.values())
herbsByType.put(t, new HashSet<Herb>());
for (Herb h : garden)
herbsByType.get(h.type).add(h);
System.out.println(herbsByType);
同样进行多次索引(数组的数组),道理是一样的
总而言之,最好要用序数来索引数组,而要是有EnumMap。
多维的汝EnumMap<..., EnumMap<...>>
第34条:用接口模拟可伸缩的枚举
虽然无法编写可扩展的枚举类型,却可以通过编写接口以及实现该接口的基础枚举类型,对它进行模拟。
如对于操作Operation,在基本操作枚举已有的情况下,假如需要添加更多的操作,可以编写扩展的枚举来实现这个接口,针对基础枚举的API是根据接口来编写的,对扩展的枚举同样适用。
基础的Operation
// Emulated extensible enum using an interface
public interface Operation {
double apply(double x, double y);
}
public enum BasicOperation implements Operation {
PLUS("+") {
public double apply(double x, double y) { return x + y; }
},
MINUS("-") {
public double apply(double x, double y) { return x - y; }
},
TIMES("*") {
public double apply(double x, double y) { return x * y; }
},
DIVIDE("/") {
public double apply(double x, double y) { return x / y; }
};
private final String symbol;
BasicOperation(String symbol) {
this.symbol = symbol;
}
@Override public String toString() {
return symbol;
}
}
扩展的Operation
// Emulated extension enum
public enum ExtendedOperation implements Operation {
EXP("^") {
public double apply(double x, double y) {
return Math.pow(x, y);
}
},
REMAINDER("%") {
public double apply(double x, double y) {
return x % y;
}
};
private final String symbol;
ExtendedOperation(String symbol) {
this.symbol = symbol;
}
@Override public String toString() {
return symbol;
}
}
第35条:注解优先于命名模式
目前使用很少,没有什么深刻体会,理解不透
第36条:坚持使用Override注解
在你想要覆盖超类声明的每个方法中声明使用Override注解,这样编译器就会帮助你发现是否正确覆盖了一个方法。
例外:在具体的类中,不必标注你确信覆盖了的抽象的方法;实现接口的类中,也不必标注出你想要哪些方法来覆盖接口方法,这两种情况编译器都会帮助你提醒,没有覆盖,抛出错误。当然标注了也没有什么坏处。
但是在抽象类和接口覆盖超类或者超接口的时候,坚持使用Override注解
第37条:用标记接口定义类型
不知所云
上一条:Effective Java 2 读书笔记 第6章 枚举和注解
分享到:
相关推荐
effective java 读书笔记,第二版自己摘要并翻译,以备速查。
Effective Java读书笔记.pdf
Effective-Java读书笔记,别人分享的
《Effective Java》读书分享.pptx
2021年EFFECTIVEJAVA读书笔记.docx
effective c++读书笔记和总结 effective c++读书笔记和总结
基于Effective Java第二版的读书笔记,可以帮助你更好的阅读这边Java圣经,更快的理解其思想
Finix的精版Effective STL读书笔记
effectiveJava的笔记
effective C++读书笔记,effective C++读书笔记,effective C++读书笔记
《Effective Java》第三版中文版目录 第一章 介绍 1 第二章 创建和销毁对象 4 1 考虑用静态工厂方法替换构造器 4 2 当遇到多个构造器参
Effective Java读书笔记,记载了大部分我觉的有用的东西,前半部分有代码说明,但后半部分的代码,太过琐碎,就没有整理
【Effective Java】阅读笔记markdown 文件
effective C++ 很好 很不错 最好的C++进阶资料
Effective java 3 学习记录
《Effective Modern C++:改善C++11和C++14的42个具体做法(影印版)(英文版)》中包括以下主题:剖析花括号初始化、noexcept规范、完美转发、智能指针make函数的优缺点;讲解std∷move,std∷forward,rvalue引用和全局...
effective-java 自己用网上的翻译的html版本,生成的pdf版,仅供参考,仅供参考,仅供参考,仅供参考
Effective Java Effective Java Effective Java
记录了我的effective-Python学习笔记,精简了effective-Python中重要的部分。effective-Python是一本值得多看几遍的书,但是看后面的几遍的时候完全可以直接看自己的学习笔记。此学习笔记侧重与比较实用的部分即前四...
Effective C++ Roy T 读书笔记,比较老的一本书,探讨一下C++。