JavaSE : 枚举 Enum

1.基本概念

枚举(enum)是Java中的一种特殊数据类型,它允许你定义一组固定的常量。这些常量代表了一组预定义的值集合,使得代码更加清晰、安全且易于维护。

简单说就是这个类只有这几个实例。

枚举是从Java 1.5版本开始引入的,通过使用enum关键字来定义。

1.1.定义枚举

枚举的基本语法如下:

enum Color {
    RED,
    GREEN,
    BLANK,
    YELLOW
}

在这个例子中,Color是一个枚举类型,它包含了三个枚举常量:REDGREENBLUE

1.2.使用枚举

枚举类型的每个实例都是一个枚举成员。在枚举中,成员默认是public static final的,这意味着它们是常量,并且可以在枚举类型之外直接访问。

枚举常量可以在switch语句中使用,使得基于不同枚举值执行不同逻辑变得非常直接和清晰。

Color color = Color.RED; // 直接取值

switch (color) {
    case RED:
        System.out.println("红色");
        break;
    case GREEN:
        System.out.println("绿色");
        break;
    case BLANK:
        System.out.println("黑色");
        break;
    case YELLOW:
        System.out.println("黄色");
        break;
    default:
        System.out.println("默认");
        break;
}

1.3.特点与优势

  1. 类型安全:枚举保证了变量只能是预定义的常量之一,避免了非法值的赋值错误。

  2. 唯一性:每个枚举常量都是唯一的实例,即使它们的名称相同,也是不同的对象。

  3. 可读性和可维护性:枚举提供了自我解释的代码,提高了代码的可读性和维护性。

  4. 可迭代性:枚举类型可以被遍历,方便进行循环操作。

  5. 支持方法和字段:枚举可以定义自己的方法和属性,就像一个普通的Java类一样。

  6. 实现接口:枚举类型可以实现接口,并为接口中的方法提供实现。

  7. 序化支持:枚举类型自动支持序列化。

2.构造,方法与属性

枚举中的每个常量都可以有自己的特定属性和行为。

枚举可以有构造方法, 并且可以传递参数给它们。这些构造器总是私有的,以确保枚举的实例只能通过声明的枚举成员来创建:

例如:

public enum Color {
    /**
     * 这是相当于通过调用构造方法得到对应的实例, 并直接为实例命名
     * 每个实例使用 , 逗号连接, 最后使用 ; 分号结尾
     */

    RED("红色"), GREEN("绿色"), BLANK("黑色"), YELLOW("黄色");

    // 成员变量
    private String meaning;

    // 构造方法 可以传递参数给它们。这些构造器总是私有的,以确保枚举的实例只能通过声明的枚举成员来创建:
    private Color(String meaning) {
        this.meaning = meaning;
    }

    public String getMeaning() {
        return meaning;
    }
}

2.1.枚举常用方法

public static void main(String[] args) {
    // 0、name(),枚举名称
    System.out.println("枚举名称," + Color.RED.name());

    // 1、ordinal(),枚举顺序值
    System.out.println("枚举顺序值," + Color.RED.ordinal()); // 0 , 序号从0开始

    // 2、values()
    // 遍历枚举 .values() 是枚举的静态方法
    for (Color value : Color.values()) {
        System.out.println("value = " + value);
        System.out.println("value.getMeaning() = " + value.getMeaning());
    }


    // 3、valueOf() 可以通过枚举常量的名称将其转换为枚举实例。
    // 如果指定的名称不存在,则此方法会抛出IllegalArgumentException。
    Color c1 = Enum.valueOf(Color.class, "RED");
    Color c2 = Color.valueOf("RED");
    System.out.println("Enum.valueOf: " + c1); //RED
    System.out.println("Color.valueOf: " + c2); //RED


    // 4、通过compareTo方法比较,实际上其内部是通过ordinal()值比较的
    System.out.println("Color.RED.compareTo(Color.BLANK) : " + Color.RED.compareTo(Color.BLANK)); // -2


}

3.实现接口

在Java中,枚举(enum)不仅可以定义一系列固定的常量,还可以实现接口,这意味着每个枚举常量都可以作为接口的一个实现。这种方式结合了枚举的安全性和接口的多态性,使得代码更加灵活和强大。

增强功能:通过实现接口,可以让枚举不仅仅代表一种状态或选项,还可以具有行为,即实现接口中定义的方法。

多态性:枚举实现接口后,可以将枚举实例当作接口类型的对象来使用,利用多态性编写更通用的代码。

代码复用:接口可以被多个枚举共享,减少重复代码,提高代码的复用性。

3.1.示例一

首先,我们需要定义一个接口,这个接口将声明一些方法,稍后我们的枚举类型将会实现这些方法。

public interface AnimalSound {
    void makeSound(); // 定义一个方法,让实现类(在这里是枚举)提供具体实现
}

枚举实现接口

接下来,我们定义一个枚举类型Animal,让它实现上面定义的AnimalSound接口:

public enum Animal implements AnimalSound {
    DOG {
        @Override
        public void makeSound() {
            System.out.println("Woof!");
        }
    },
    CAT {
        @Override
        public void makeSound() {
            System.out.println("Meow!");
        }
    },
    COW {
        @Override
        public void makeSound() {
            System.out.println("Moo!");
        }
    };

}

在上面的例子中,Animal枚举有三个实例:DOGCATCOW,每个实例都实现了AnimalSound接口中的makeSound方法,提供了各自特有的实现。

使用枚举实现接口

现在,我们可以在程序中使用这些枚举实例,并通过接口调用它们实现的方法:

public class Main {
    public static void main(String[] args) {
        Animal animal = Animal.DOG;
        animal.makeSound(); // 输出: Woof!

        // 或者通过循环遍历所有枚举值,展示每种动物的声音
        for (Animal a : Animal.values()) {
            a.makeSound();
        }
    }
}

在这个例子中,Main类展示了如何通过接口方法调用枚举实例的特定行为。通过这种方式,枚举不仅定义了一组固定的常量,还通过实现接口为这些常量赋予了行为,使得代码更加灵活和强大。

3.2.示例二

定义接口

public interface ColorInfo {
    String getColorNum();
}

实现接口的枚举

public enum Color implements ColorInfo {

    RED("红色") {
        @Override
        public String getColorNum() {
           return "#ff0000";
        }
    }, 
    GREEN("绿色") {
        @Override
        public String getColorNum() {
            return "#00ff00";
        }
    }, 
    BLANK("黑色") {
        @Override
        public String getColorNum() {
           return "#000000";
        }
    }, 
    YELLOW("黄色") {
        @Override
        public String getColorNum() {
           return "#FFF200";
        }
    };

    // 成员变量 为终态
    private final String meaning;
    
    private Color(String meaning) {
        this.meaning = meaning;
    }

    public String getMeaning() {
        return meaning;
    }
}

测试代码

    public static void main(String[] args) {

        ColorInfo colorInfo = Color.RED;
        System.out.println(colorInfo.getColorNum());

        Color green = Color.GREEN;
        System.out.println(green.getMeaning());
        System.out.println(green.getColorNum());

    }

3.3.示例三

假设我们有一个需求,定义一个枚举表示一周中的几天,并要求这些天可以报告它们的名字以及是否是工作日。我们可以定义一个接口Workable来表示是否工作,然后让枚举DayOfWeek实现这个接口。

定义接口

public interface Workable {
    boolean isWorkingDay();
}

实现接口的枚举

public enum DayOfWeek implements Workable {
    MONDAY(true),
    TUESDAY(true),
    WEDNESDAY(true),
    THURSDAY(true),
    FRIDAY(true),
    SATURDAY(false),
    SUNDAY(false);

    private final boolean workingDay;

    DayOfWeek(boolean workingDay) {
        this.workingDay = workingDay;
    }

    @Override
    public boolean isWorkingDay() {
        return workingDay;
    }

    @Override
    public String toString() {
        return super.toString().toLowerCase();
    }
}

在这个例子中,DayOfWeek枚举实现了Workable接口,并提供了isWorkingDay方法的实现。每个枚举常量在构造时指定了它是否为工作日。

现在,你可以像使用任何实现了Workable接口的对象那样使用DayOfWeek的枚举常量,享受多态带来的好处。

public class EnumTest{
    public static void main(String[] args) {
        for (DayOfWeek day : DayOfWeek.values()) {
            System.out.println( day + " 是否为工作日 : " + day.isWorkingDay());
        }
        
        // 使用接口类型引用
        Workable day = DayOfWeek.MONDAY;
        System.out.println(day.isWorkingDay()); // 输出: true
    }
}

通过让枚举实现接口,我们不仅定义了一组固定常量,还赋予了它们行为,增强了代码的表达力和灵活性。

4.枚举与switch表达式

在Java 12及之后的版本中,引入了一项名为“Switch Expressions”的新特性,它扩展了传统switch语句的功能,使得switch不仅可以用作语句,也可以作为表达式使用,特别适合与枚举结合,使得代码更加简洁和表达力更强。以下是这种新用法的一些亮点:

假设我们定义了一个表示季节的枚举类型:

public enum Season {
    SPRING, SUMMER, FALL, WINTER
}

4.1.基本用法

public class WeatherReport {
    public static void describeSeason(Season season) {
        switch (season) {
            case SPRING:
                System.out.println("春天,万物复苏,花开满园。");
                break;
            case SUMMER:
                System.out.println("夏天,阳光灿烂,热情似火。");
                break;
            case FALL:
                System.out.println("秋天,金风送爽,硕果累累。");
                break;
            case WINTER:
                System.out.println("冬天,白雪皑皑,银装素裹。");
                break;
            // 默认情况下,如果添加了新的枚举值而忘记更新switch,编译器会提示错误,保证了代码的完整性。
            default:
                throw new IllegalArgumentException("未知的季节: " + season);
        }
    }

    public static void main(String[] args) {
        describeSeason(Season.SPRING); // 输出: 春天,万物复苏,花开满园。
    }
}

4.2.新的Switch表达式

在Java 12及以后版本中,switch可以作为表达式直接返回一个值,这在处理枚举时特别有用:

public class SeasonExample {
    public static String getSeasonDescription(Season season) {
        return switch (season) {
            case SPRING -> "春天,万物复苏,花开满园。";
            case SUMMER -> "夏天,阳光灿烂,热情似火。";
            case FALL -> "秋天,金风送爽,硕果累累。";
            case WINTER -> "冬天,白雪皑皑,银装素裹。";
            // Java 14及以后版本支持密封类(sealed classes)和permits关键字来限定哪些类可以继承,进一步增强类型安全
            // 在此例中,由于枚举天然封闭,不需要额外密封类特性
        };
    }

    public static void main(String[] args) {
        System.out.println(getSeasonDescription(Season.SUMMER)); // 输出: 夏天,阳光灿烂,热情似火。
    }
}

特性总结

  1. 箭头语法(->:简化了case分支的写法,每个分支直接跟随一个表达式,不再需要break语句。
  2. 更简洁的语法:无需显式地返回值或使用break,使得代码更紧凑。
  3. 多值匹配(Java 14+):可以匹配多个枚举值,使用逗号分隔,例如CASE SPRING, SUMMER -> ...
  4. 模式匹配(Java 16+):增加了更复杂的匹配模式,尽管这主要针对类实例而非枚举,但增强了switch表达式的灵活性。
  5. yield关键字:在需要的地方使用yield来输出结果值,类似于return,但在没有yield的情况下最后一个表达式自动作为返回值(如上例所示)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值