本章主要介绍在使用枚举和注解这两种类型时要注意的问题。

第三十条 用enum代替int常量

本条主要是讲枚举类型enum的优缺点。 ### int常量为什么不好? 枚举类型还不存在之前(JDK1.5才引入枚举类型),大多数程序都使用下面的int常量形式代替枚举:

public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;

它的缺点如下: - 没有类型安全可言,即使误传了编译器也不会给予任何警告 - 没有自己的命名空间,导致不同的枚举类型必须加上前缀区分,比如上面的APPLE_就是前缀 - 无法打印出int常量的名字,导致在日志中看到的是int常量的数字而不是友好可读的字符串。 - 有人将int常量用String常量代替来解决上条的打印问题,然而,基于字符串的枚举常量在比较的时候性能糟糕,而且很容易被人误用字面量代替,导致产生大量硬编码。何况字符串的常量也不能解决前两个问题。

枚举类型的优点

优点如下: - 类型安全 - 有自己的命名空间 - 实例受控,每个枚举类型的枚举元素都是全局唯一的,且为final的,客户端既不能新建也不能修改或者继承它们(基于这个特性,在比较两个枚举元素的时候,你可以直接使用==运算符,因为每个枚举元素的对象都是全局唯一的,如果两个枚举元素相等,那么它们的对象肯定是同一个对象。反之亦然) - 允许添加任意的方法和域 - 枚举类型也可以实现接口,可以有抽象方法 - 枚举类型默认重写了toString方法,使你可以直接打印出枚举的名称

枚举的例子

给一个枚举的例子出来:

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
    }
}

枚举的缺点

装载和初始化枚举时,有空间和时间的成本。但是,如书中所说,除了受资源约束的设备,如手机和烤面包机,在实践中不必在意这个问题……

第三十一条 用实例域代替序数

本条讲枚举类型默认带一个方法:ordinal(),这个方法返回枚举常量在枚举中定义的时候的序号(当然是从0开始的)。 然后很多人非常愉快地使用这个方法来返回常量的序号,并用这个序号做逻辑。 书中指出:不建议这么做。 因为枚举常量的位置会变,这是无法保证的。而且想要再添加一个与已经用过的序号有所关联的枚举常量,就难了。 应该使用实例域来代替这种不靠谱的方法:

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;
    }
}

第三十二条 用EnumSet代替位域

本条主要讲的是JDK提供了一个叫做EnumSet的类,可以为我们提供多个枚举常量的集合,并且轻松实现交集并集等操作。 它的底层在枚举元素数小于64个时,使用的是long类型实现的位域来进行操作的,因此在小于64个元素的时候,它和传统的利用位域实现的集合性能相当。

第三十三条 用EnumMap代替序数索引

本条主要讲的是jdk提供了一个叫做EnumMap的类,如果我们要用枚举作为键来归类某些东西,最好使用Enummap这个类而不是用ordinal方法去得到枚举常量的序数来作为键。

第三十四条 用接口模拟可伸缩的枚举