`
xusulong
  • 浏览: 79774 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Effective Java 2 读书笔记 第6章 枚举和注解

阅读更多

第30条:用enum代替int常量

枚举类型对比int枚举,String枚举等

  1. 易读(不同枚举可以包含同名等)
  2. 更加安全(不同枚举值不可以互操作)
  3. 功能更加强大(可以添加任意的方法和域,并实现任意接口,为了将数据与枚举常量关联起来,得声明实例域,并编写一个带有数据并将数据保存在域中的构造器)

可以将数据与枚举常量关联起来,得声明实例域,并编写一个带有数据并将数据保存在域中的构造器

 

可以将不同行为与每个枚举常量关联起来,特定于常量的类主体

 

如果多个枚举常量同时共享相同的行为,则考虑策略枚举,而不是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章 枚举和注解

 

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics