博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《Effective Java》读书笔记五(枚举和注解)
阅读量:6221 次
发布时间:2019-06-21

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

No30 用enum代替int常量

一:综述

int枚举模式,示范:

// The int enum pattern - severely deficient!public static final int APPLE_FUJI             =  0;public static final int APPLE_PIPPIN           =  1;public static final int APPLE_GRANNY_SMITH     =  2;

还有一种是这种模式的变体,使用String常量代替int常量,称为String枚举模式。缺点:

  • int枚举是编译时常量,如果它发生了变化,客户端需要重新编译;
  • 将int枚举常量翻译成可打印的字符串,并没有很便利的方法,你所见到的就是一个数字(比如调试,跟踪值等);
  • String枚举中的硬编码常量如果包含有书写错误,那么这样的错误在编译时不会被检测到,运行时会出错。

JDK1.5发行版本开始,提出了另一种可以替代的解决方案,可以避免int和String枚举模式的缺点,并提供许多额外的好处。示范:

public enum Apple {FUJI, PIPPIN, GARNNY_SMITH}
  • 枚举提供了编译时的类型安全。如果声明一个参数的类型为Apple,就可以保证,被传到该参数上的任何非null的对象引用一定属于三个有效的Apple值之一。
  • 常量值没有被编译到客户端代码中;
  • 允许添加任意的方法和域,并实现任意的接口。

二:有关枚举的一个好例子:

// Enum type with data and behavior - Pages 149-150public enum Planet {    MERCURY(3.302e+23, 2.439e6),    VENUS  (4.869e+24, 6.052e6),    EARTH  (5.975e+24, 6.378e6),    MARS   (6.419e+23, 3.393e6),    JUPITER(1.899e+27, 7.149e7),    SATURN (5.685e+26, 6.027e7),    URANUS (8.683e+25, 2.556e7),    NEPTUNE(1.024e+26, 2.477e7);    private final double mass;           // In kilograms    private final double radius;         // In meters    private final double surfaceGravity; // In m / s^2    // Universal gravitational constant in m^3 / kg s^2    private static final double G = 6.67300E-11;    // Constructor    Planet(double mass, double radius) {        this.mass = mass;        this.radius = radius;        surfaceGravity = G * mass / (radius * radius);    }    public double mass()           { return mass; }    public double radius()         { return radius; }    public double surfaceGravity() { return surfaceGravity; }    public double surfaceWeight(double mass) {        return mass * surfaceGravity;  // F = ma    }}
// Takes earth-weight and prints table of weights on all planets - Page 150public class WeightTable {   public static void main(String[] args) {           if(args.length == 0) {               args = new String[]{"100"};           }                 double earthWeight = Double.parseDouble(args[0]);      double mass = earthWeight / Planet.EARTH.surfaceGravity();      for (Planet p : Planet.values())          System.out.printf("Weight on %s is %f%n",                            p, p.surfaceWeight(mass));                Planet p = Planet.MERCURY;      System.out.printf("dd Weight on %s is %f%n",                        p, p.surfaceWeight(mass));   }}

三:复杂一些的例子,比较如下两段代码:

代码一:

// Enum type that switches on its own value – questionablepublic enum Operation {         PLUS, MINUS, TIMES, DIVIDE;                // Do the arithmetic op represented by this constant         double apply(double x, double y) {                   switch(this) {                            case PLUS:  return x + y;                            case MINUS: return x - y;                            case TIMES: return x * y;                            case DIVIDE:return x / y;                   }                   throw new AssertionError("Unknown op:" + this);         }}

上述代码可行,但是不太好看。如果没有throw语句,它就不能进行编译。另外,如果添加了新的枚举常量,却忘记给switch添加相应的条件,枚举仍然可以编译,但当你试图运用新的运算时,就会运行失败。

代码二:

// Enum type with constant-specific class bodies and data - Page 153import java.util.*;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; }    };    private final String symbol;    Operation(String symbol) { this.symbol = symbol; }    @Override public String toString() { return symbol; }    abstract double apply(double x, double y);    // Implementing a fromString method on an enum type - Page 154    private static final Map
stringToEnum = new HashMap
(); static { // Initialize map from constant name to enum constant for (Operation op : values()) stringToEnum.put(op.toString(), op); } // Returns Operation for string, or null if string is invalid public static Operation fromString(String symbol) { return stringToEnum.get(symbol); } // Test program to perform all operations on given operands public static void main(String[] args) { double x = Double.parseDouble(args[0]); double y = Double.parseDouble(args[1]); for (Operation op : Operation.values()) System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y)); }}

输出:

2.000000 + 4.000000 = 6.000000

2.000000 - 4.000000 = -2.000000

2.000000 * 4.000000 = 8.000000

2.000000 / 4.000000 = 0.500000

注:枚举类型中的抽象方法必须被它所有常量中的具体方法覆盖。

No32 用EnumSet代替位域

如果一个枚举类型的元素主要用在集合中,一般就使用int枚举模式,将2的不同倍数赋予每个常量:

// Bit field enumeration constants – OBSOLETE!import java.util.*;public class Text {    public static final int STYLE_BOLD          = 1 << 0;       //1    public static final int STYLE_ITATIC         = 1 << 1;       //2    public static final int STYLE_UNDERLINE     = 1 << 2;       //4    public static final int STYLE_STRIKETHROUGH = 1 << 3;       //8      //Parameter is bitwise OR of zero or more STYLE_ constants    public void applyStyles(int styles){        // 根据styles值进行拆分,判断是哪些参数...    };    // Sample use    public static void main(String[] args) {        Text text = new Text();        text.applyStyles(STYLE_BOLD|STYLE_ITATIC);    }   }

下面是一个范例改成枚举代替位域后的代码,它更加简洁、更加清楚、也更加安全。

// EnumSet - a modern replacement for bit fields - Page 160import java.util.*;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

No36 坚持使用Override注解

如果你能判断出下面的代码输出结果是260,而不是26,那么你的水平应该足够高。即使这样,也不应该忽略Override注解。

// Can you spot the bug? - Page 176import java.util.*;public class Bigram {    private final char first;    private final char second;    public Bigram(char first, char second) {        this.first  = first;        this.second = second;    }    public boolean equals(Bigram b) {        return b.first == first && b.second == second;    }    public int hashCode() {        return 31 * first + second;    }    public static void main(String[] args) {        Set
s = new HashSet
(); for (int i = 0; i < 10; i++) for (char ch = 'a'; ch <= 'z'; ch++) s.add(new Bigram(ch, ch)); System.out.println(s.size()); }}

在上面的代码中的方法equals之前增加@Override注解,则编译出错!因为equals需要的是Object参数,而不是Bigram参数。这样,你应该能够明白@Override的作用了吧。

 

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

你可能感兴趣的文章
异常处理
查看>>
家庭记事本开发进度2
查看>>
[Azure] 使用 Visual Studio 2013 管理中国版 Azure 订阅
查看>>
VB动态添加WebBrowser控件,并拦截弹出窗口(不用引用任何组件)
查看>>
C#线程的参数传递
查看>>
HDU1059
查看>>
jquery 选择器多个
查看>>
关于Maya 的 Node ID
查看>>
[转]DPM2012系列之十一:还原exchange 2010数据库
查看>>
Vector、ArrayList、List使用深入剖析
查看>>
教孩子学编程 Python
查看>>
:s 命令来替换字符串
查看>>
【“零起点”--百度地图手机SDK】如何查询公交线路?如北京的104路
查看>>
C#使用Linq操作Xml文件(创建xml文件、增删改查xml文件节点信息)的方法
查看>>
Jmeter:图形界面压力测试工具
查看>>
proteus中的常用文件
查看>>
Ubuntu搜狗输入法无法输入中文等问题
查看>>
Linux函数之snprintf()[一]
查看>>
php 使用zendstudio 生成webservice文件 wsdl
查看>>
mysql将数据表改成 innodb
查看>>