# Java 基础 - 枚举

## 1. 基础

### 1.1 基本用法

定义：`enum`

```java
public enum Size {
    SMALL, MEDIUM, LARGE
}
```

1. `toString()`方法返回其字面值，所有枚举类型也都有一个name()方法，返回值与toString()一样。

   ```java
   Size size = Size.SMALL;
   System.out.println(size.toString());
   System.out.println(size.name());
   ----
   Output: "SMALL"  
   ```
2. `valueOf(String)`返回字符串对应的枚举值。

   ```java
   System.out.println(Size.SMALL==Size.valueOf("SMALL"));
   ---
   Output: true
   ```
3. `values()`返回一个包括所有枚举值的数组，顺序与声明时的顺序一致。

   ```java
   for(Size size : Size.values()){
       System.out.println(size);
   }
   ---
   Output: SMALL, MEDIUM, LARGE
   ```
4. `int ordinal()`表示枚举值在声明时的顺序，从0开始。

   ```java
   Size size = Size.MEDIUM;
   System.out.println(size.ordinal());
   ---
   Output: 1
   ```

### 1.2 枚举的好处

* 定义枚举的语法更为简洁。
* **枚举更为安全**，一个枚举类型的变量，它的值要么为null，要么为枚举值之一，不可能为其他值，但使用整形变量，它的值就没有办法强制，值可能就是无效的。
* 枚举类型自带很多便利方法(如values, valueOf, toString等)，易于使用。

### 1.3 实现原理

**枚举类型实际上会被Java编译器转换为一个对应的类，这个类继承了Java API中的`java.lang.Enum`类。**

Enum类有两个实例变量`name`和`ordinal`，在构造方法中需要传递，`name(), toString(), ordinal(), compareTo(), equals()`方法都是由Enum类根据其实例变量name和ordinal实现的。

values和valueOf方法是编译器给每个枚举类型自动添加的，上面的枚举类型Size转换后的普通类的代码大概如下所示：

```java
public final class Size extends Enum<Size> {
    public static final Size SMALL = new Size("SMALL",0);
    public static final Size MEDIUM = new Size("MEDIUM",1);
    public static final Size LARGE = new Size("LARGE",2);
    
    private static Size[] VALUES =
            new Size[]{SMALL,MEDIUM,LARGE};
    
    private Size(String name, int ordinal){
        super(name, ordinal);
    }
    
    public static Size[] values(){
        Size[] values = new Size[VALUES.length];
        System.arraycopy(VALUES, 0,
                values, 0, VALUES.length);
        return values;
    }
    
    public static Size valueOf(String name){
        return Enum.valueOf(Size.class, name);
    }
}
```

* Size是final的，不能被继承，`Enum<Size>`表示父类。
* Size有一个私有的构造方法，接受`name`和`ordinal`，传递给父类，私有表示不能在外部创建新的实例。
* 三个枚举值实际上是三个静态变量，也是final的，不能被修改。
* values方法是编译器添加的，内部有一个`values数组`保持所有枚举值。
* valueOf方法调用的是父类的方法，额外传递了参数Size.class，表示类的类型信息，父类实际上是回过头来调用values方法，根据name对比得到对应的枚举值的。

## 2. 典型场景

```java
public enum Size {
    SMALL("S","小号"),
    MEDIUM("M","中号"),
    LARGE("L","大号");
    
    private String abbr;
    private String title;
    
    private Size(String abbr, String title){
        this.abbr = abbr;
        this.title = title;
    }

    public String getAbbr() {
        return abbr;
    }

    public String getTitle() {
        return title;
    }
    
    public static Size fromAbbr(String abbr){
        for(Size size : Size.values()){
            if(size.getAbbr().equals(abbr)){
                return size;
            }
        }
        return null;
    }
}
```

### 2.2 实现原理

```java
public final class Size extends Enum<Size> {
  public static final Size SMALL =        
          new Size("SMALL",0, "S", "小号");     
  public static final Size MEDIUM =       
          new Size("MEDIUM",1,"M","中号");      
  public static final Size LARGE =        
          new Size("LARGE",2,"L","大号");       
                                          
  private String abbr;                    
  private String title;                   
                                          
  private Size(String name, int ordinal,  
          String abbr, String title){         
      super(name, ordinal);               
      this.abbr = abbr;                   
      this.title = title;                 
  }
  //... 其他代码
}
```

每个枚举值经常有一个关联的标示(id)，通常用int整数表示，使用整数可以节约存储空间，减少网络传输。一个自然的想法是使用枚举中自带的ordinal值，但ordinal并不是一个好的选择。

为什么呢？因为ordinal的值会随着枚举值在定义中的位置变化而变化，但一般来说，我们希望id值和枚举值的关系保持不变，尤其是表示枚举值的id已经保存在了很多地方的时候。

## 3. 高级用法

枚举还有一些高级用法，比如说，**每个枚举值可以有关联的类定义体，枚举类型可以声明抽象方法，每个枚举值中可以实现该方法，也可以重写枚举类型的其他方法。**

```java
public enum Size {
    SMALL {
        @Override
        public void onChosen() {
            System.out.println("chosen small");
        }
    },MEDIUM {
        @Override
        public void onChosen() {
            System.out.println("chosen medium");
        }
    },LARGE {
        @Override
        public void onChosen() {
            System.out.println("chosen large");
        }
    };
    
    public abstract void onChosen();
}
```

这种写法内部是怎么实现的呢？每个枚举值都会生成一个类，这个类继承了枚举类型对应的类，然后再加上值特定的类定义体代码，枚举值会变成这个子类的对象，具体代码我们就不赘述了。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ldbmcs.gitbook.io/java/java-39/java-ji-chu/java-ji-chu-mei-ju.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
