• 《Effective Java》读书笔记五(枚举和注解)


    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-150
    public 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 150
    public 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 – questionable
    public 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 153
    import 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<String, Operation> stringToEnum
            = new HashMap<String, Operation>();
        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 160
    import 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<Style> styles) {
            // Body goes here
        }
    
        // Sample use
        public static void main(String[] args) {
            Text text = new Text();
            text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
        }
    }

    No36 坚持使用Override注解

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

    // Can you spot the bug? - Page 176
    import 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<Bigram> s = new HashSet<Bigram>();
            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的作用了吧。

  • 相关阅读:
    C# 查找其他应用程序并打开、显示、隐藏、关闭的API
    微信公众平台开发2-access_token获取及应用(含源码)
    Winform下编译Dev控件时提示license.licx文件错误
    JS+MySQL获取 京东 省市区 地区
    MySQL性能调优与架构设计——第11章 常用存储引擎优化
    MySQL性能调优与架构设计——第10章 MySQL数据库Schema设计的性能优化
    MySQL性能调优与架构设计——第9章 MySQL数据库Schema设计的性能优化
    MySQL性能调优与架构设计——第1章 MySQL 基本介绍
    .NET基础 (21)ASP NET应用开发
    .NET基础 (20).NET中的数据库开发
  • 原文地址:https://www.cnblogs.com/nayitian/p/3245516.html
Copyright © 2020-2023  润新知