如何用枚举消除if/else?-枚举高阶用法

1 篇文章 0 订阅
1 篇文章 0 订阅

枚举特性

我们先来写一个枚举

public enum TestEnum {
    //TEACHER 老师;STUDENT 学生;PARENT 父母
    TEACHER,STUDENT,PARENT
}

然后编译成class文件,再反编译class文件。

PS E:\Study\code\demo\src\main\java\com\lvshen\demo\enumtest> javac .\TestEnum.java
PS E:\Study\code\demo\src\main\java\com\lvshen\demo\enumtest> javap -p .\TestEnum.class
Compiled from "TestEnum.java"

//代码区
public final class com.lvshen.demo.enumtest.TestEnum extends java.lang.Enum<com.lvshen.demo.enumtest.TestEnum> {
  public static final com.lvshen.demo.enumtest.TestEnum TEACHER;
  public static final com.lvshen.demo.enumtest.TestEnum STUDENT;
  public static final com.lvshen.demo.enumtest.TestEnum PARENT;
  private static final com.lvshen.demo.enumtest.TestEnum[] $VALUES;
  public static com.lvshen.demo.enumtest.TestEnum[] values();
  public static com.lvshen.demo.enumtest.TestEnum valueOf(java.lang.String);
  //构造函数
  private com.lvshen.demo.enumtest.TestEnum();
  static {};
}

我们发现枚举有这几个特性:

(1)枚举TestEnumfinal修饰,并且默认继承了Enum类。因此不能再继承其他的类。

(2)枚举的构造函数是private修饰的,所以不能通过构造函数获取对象。

(3)枚举的属性是static修饰的,可以通过枚举直接调用属性。

(4)valueOf(java.lang.String)可以通过枚举的名称获取对应的实例。

还有一个重要的特性,对于(2)虽然不能直接获取构造对象,你可能会有疑问,我反射暴力获取可以吗?答案是不可以。我们看看反射是怎么做的。

Class<?> aClass = Class.forName("xx.xx.xx");
Constructor<?> constructor = aClass.getDeclaredConstructor(String.class);
TestEnum test = (TestEnum) constructor.newInstance("");

我们来看看newInstance

if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");

如果是枚举,你想暴力反射获取对象,直接抛异常。

我们能用枚举做什么

消除烦人的if/else

当你代码中的if/else过多的时候,会提高你代码的复杂度,如果你们公司对sonar异味有要求,肯定被if/else困扰过。

我们可以用枚举消除if/else,使用场景如下:

if ("PANDA".equals(type)) {
    System.out.println("吃竹子");
} else if("CAT".equals(type)) {
    System.out.println("吃鱼");
} else if("MONKEY".equals(type)) {
    System.out.println("吃香蕉");
}

当输入type,输出对应结果。例如当type = "CAT"时,输出"吃鱼"。你肯定会想到用上面的if/else方法。我们用枚举怎么做呢?

首先创建接口,定义通用方法

public interface Common {
    //吃
    String eat();
}

创建枚举实现这个方法

public enum AnimalEnum implements Common {
    PANDA {
        @Override
        public String eat() {
            return "吃竹子";
        }
    },
    CAT {
        @Override
        public String eat() {
            return "吃鱼";
        }
    },
    MONKEY{
        @Override
        public String eat() {
            return "吃香蕉";
        }
    }
}

然后调用

String type = "CAT";
String eat = AnimalEnum.valueOf(type).eat();
System.out.println(eat);

type = "CAT"时,输出"吃鱼"。至此我们消除了if/else

用枚举实现单列

《 Effective Java》作者大力推荐的方式。

这种方式不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化和反射攻击重新创建新的对象,绝对防止多次实例化。

我们来看代码

public enum  EnumSingleton {
    INSTANCE;
    
    public  EnumSingleton getInstance() {
        return INSTANCE;
    }
}

我们起两个线程来测试

public void test1() {
    EnumSingleton instance1 = EnumSingleton.INSTANCE.getInstance();
    EnumSingleton instance2 = EnumSingleton.INSTANCE.getInstance();
    System.out.println(instance1 == instance2);
}

运行结果为True

由之前的反编译可知,属性INSTANCE 被声明为static的。枚举实现实例化时是线程安全。

Java 规范中规定,每一个枚举类型及其定义的枚举变量在 JVM 中都是唯一的,并且在枚举类型的序列化和反序列化上,Java 做了特殊的规定。在序列化的时候 Java 仅仅是将枚举对象的 name 属性输出到结果中,反序列化的时候则是通过 java.lang.Enum 的 valueOf() 方法来根据名字查找枚举对象,因此反序列化后的实例也会和之前被序列化的对象实例相同。

不过个人觉得想要实现单例就要将这个对象设计成枚举类型的,虽然安全可靠,但还是不优雅。

EnumMap

如果你需要存储key-value格式的数据,并且这个key来源于一个枚举类,那么使用EnumMap而不是HashMapEnumMap拥有更优良的性能。

public enum TestEnum {
    TEACHER,STUDENT,PARENT
}

//使用
EnumMap<TestEnum, String> enumMap = new EnumMap<>(TestEnum.class);
enumMap.put(TestEnum.TEACHER,"教书");
enumMap.put(TestEnum.STUDENT,"学习");
enumMap.put(TestEnum.PARENT,"培育小孩");

String result = enumMap.get(TestEnum.PARENT);
System.out.println(result);

我们可以通过EnumMap给枚举的成员属性赋予特定行为。如上代码,result = "培育小孩"

EnumSet

public enum TestEnum {
    TEACHER,STUDENT,PARENT
}

我们定义了枚举TestEnum,如何获取所有成员属性?这是可以使用EnumSet

EnumSet enumSet = EnumSet.allOf(TestEnum.class);
System.out.println(enumSet);

控制台打印

[TEACHER, STUDENT, PARENT]

HashSet相比,EnumSet性能更优良。

往期推荐

扫码二维码,获取更多精彩。或微信搜Lvshen_9,可后台回复获取资料

1.回复"java" 获取java电子书;

2.回复"python"获取python电子书;

3.回复"算法"获取算法电子书;

4.回复"大数据"获取大数据电子书;

5.回复"spring"获取SpringBoot的学习视频。

6.回复"面试"获取一线大厂面试资料

7.回复"进阶之路"获取Java进阶之路的思维导图

8.回复"手册"获取阿里巴巴Java开发手册(嵩山终极版)

9.回复"总结"获取Java后端面试经验总结PDF版

10.回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF)

11.回复"并发导图"获取Java并发编程思维导图(xmind终极版)

另:点击【我的福利】有更多惊喜哦。

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值