Effective Java 使用enum代替int常量
⚙️

Effective Java 使用enum代替int常量

Java 支持两种特殊用途的引用类型: 一种是类, 即枚举类型; 一种是接口, 即注解类型.
枚举是由一组固定的常量组合成的合法值的类型, 例如: 一年的季节, 月份, 常用的颜色等等. 在java引入枚举之前, 通常是使用一组int常量或者string常量类表示枚举类型,
int/string常量表示枚举类型有非常明显的缺点: 类型安全问题, 没有描述性可言.

枚举定义

public enum TimeUnit { NANOSECONDS, MICROSECONDS, MILLISECONDS}
Java枚举类的思想: 通过公有的静态final域为每个枚举常量导出一个实例, 它没有可以访问的构造器, 是一个真正的final类. 想比较于int和string类型的常量, 枚举保证了编译时的类型安全, 通过final域也可以很直观的理解它所代表的意义. 除此之外它还允许添加任意方法和域并实现任何接口.

使用

为枚举添加方法

定义一个表示三种尺寸箱子的枚举.
public enum Box { SMALL(1, 1, 1), MEDIUM(10, 10, 10), BIG(100, 100, 100); final int length; final int width; final int height; final int volume; Box(int length, int width, int height) { this.length = length; this.width = width; this.height = height; volume = length * width * height; } public int length(){return length;} public int width(){return width;} public int height(){return height;} public int getAreaSize() { return length * width * 6; } }

打印所有的枚举值

所有的枚举类都有一个values方法, 它会返回按照申明顺序返回它的值数组. 例如:
public static void main(String[] args) { Box[] values = Box.values(); for (Box value : values) { System.out.println(value); } }
输出
notion image

为每个常量关联不同的行为

可以通过在枚举类中定义一个抽象方法, 在新建枚举实例的时候, 就是强制我们去实现该抽象方法.
public enum Box { SMALL(1, 1, 1){ public int apply (int x, int y) { return x * 2 + y * 2; } }, MEDIUM(10, 10, 10){ public int apply (int x, int y) { return x * 10 + y * 10; } }, BIG(100, 100, 100){ public int apply (int x, int y) { return x * 100 + y * 100; } }; } Box(int length, int width, int height) { this.length = length; this.width = width; this.height = height; volume = length * width * height; } public abstract int apply(int x, int y);

即要valueOf又要fromString

如果我们在枚举类中覆盖了toString方法, 那么我们最好编写一个fromString的方法, 用于把特定的字符串表示法变回相应的枚举.
private static final Map<String, Box> stringToEnum = Stream.of(values()).collect(Collectors.toMap(Objects::toString, e -> e)); public static Optional<Box> fromString(String box) { return Optional.ofNullable(stringToEnum.get(box)); }

如果有需要我们可以定义多个构造方法

在实际的项目开发中, 我经常遇到不同的枚举类需要为不同的属性赋值, 而不需要提供属性值的属性, 一般就处理为写个null放在那里, 实际上我们可以通过定义多个构造器来避免这种情况.
例如我定义一个车的枚举类: Car, BMW有 carplay版本信息, 而比亚迪仰望U8, 没有这个属性.
public enum Car { BMW(100), BYD, BENZ; int carplayVersion = -1; Car(int carplayVersion) { this.carplayVersion = carplayVersion; } Car() { } }

通过枚举类实现单例模式

  1. 反射安全
  1. 序列化/反序列化安全
  1. 写法简单
  1. 没有一个更有信服力的原因不去使用枚举
怎么写?
public enum EnumSingleton { INSTANCE; }
写完了.