第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 中关于枚举与注解的知识点,涵盖了枚举类型的优点、使用指南、避免使用 int 常量、使用 EnumSet 和 EnumMap 等。 枚举类型的优点 枚举类型提供了...
这本书的第三版包含了大量更新,涵盖了Java语言和平台的新发展,如Java 8和Java 9的新特性。以下是对《Effective Java》笔记中可能涉及的关键知识点的详细解读: 1. **单例模式**:书中强调了如何正确实现单例模式...
4. **第6章 枚举和注解** - 枚举的使用:强调枚举的多种用途,如枚举类型的安全性、枚举实例的遍历、枚举常量的关联方法等。 - 注解:解释注解的元数据功能,如何创建自定义注解以及处理注解的反射API。 5. **第7...
java逻辑思维笔试题Effective Java - 第三版笔记 章节索引 02 - 创建和销毁对象 03 - 所有对象通用的方法 04 - 类和接口 05 - 泛型 06 - 枚举和注释 07 - Lambda 和流 08 - 方法 09 - 通用编程 10 - 例外 11 - 并发 ...
在“学习Java的第二个项目,面试技巧和基础进阶知识”的资源中,我们可以找到一系列关于提升Java编程技能和准备面试的材料。这个压缩包可能包含了笔记、代码示例或者指导文档,帮助学习者巩固基础知识并掌握面试中的...
【C++ Primer 笔记1】 C++ 是一种强大的编程语言,它有着丰富的特性和严格的类型系统。...持续实践和阅读高质量的C++书籍,如《C++ Primer》、《Effective C++》和《STL源码剖析》,能帮助你更全面地掌握这门语言。