java枚举和常量

枚举介绍

  1. 枚举类型都隐式继承了java.lang.Enum类,因此不能继承其他类,但可以实现接口;同时枚举类是final的,所以也不能被继承。

  2. 枚举类的构造方法是私有的(java运行时创建,外部不能进行实例化);

那么什么时候应该使用枚举呢?每当需要一组固定的常量的时候,如一周的天数、一年四季等。或者是在我们编译前就知道其包含的所有值的集合。Java 1.5的枚举能满足绝大部分程序员的要求的,它的简明,易用的特点是很突出的。

枚举产生之前

下面的方法定义十分繁琐,而且容易出错。例如我们定义的int数字出现重复,编译器也不会给出任何的警示。同时,这样的操作是实在太频繁了,最终Java 5中增加了枚举类型。

package com.demo.enumDemo;

public class Season {

    public static final int SPRING = 0;
    public static final int SUMMER = 0;
    public static final int AUTUMN = 1;
    public static final int WINTER = 2;
}

枚举

定义的枚举类,默认继承Enum抽象类,好处是提供了一些函数:

// 返回枚举常量的序数,即它是第几个声明的常量(从 0 开始)
// 该方法获取的是枚举变量在枚举类中声明的顺序,下标从0开始,
//   如日期中的MONDAY在第一个位置,那么MONDAY的ordinal值就是0,
//   如果MONDAY的声明位置发生变化,那么ordinal方法获取到的值也随之变化,
// 注意在大多数情况下我们都不应该首先使用该方法,毕竟它总是变幻莫测的。
public final int ordinal()

// 使枚举常量之间可以比较大小。比较此枚举与指定对象定义时的顺序
public final int compareTo(E o)

// 返回该枚举变量所在的枚举类。
public final Class<E> getDeclaringClass()

// 提供了 values 方法,可以用来遍历所有的枚举常量。
// 提供了 valueOf 方法,可以将字符串转换为枚举常量。

// name()可以获得枚举值的名称
package com.demo.enumDemo;

public enum SeasonEnum {

    SPRING,SUMMER,AUTUMN,WINTER;
}




package com.demo.enumDemo;

public class TestEnum {

    public static void main(String[] args) {


        //名字是:SPRING,对应的索引为:0
        //名字是:SUMMER,对应的索引为:1
        //名字是:AUTUMN,对应的索引为:2
        //名字是:WINTER,对应的索引为:3
        System.out.println("名字是:" + SeasonEnum.SPRING.name() + ",对应的索引为:" + SeasonEnum.SPRING.ordinal());
        System.out.println("名字是:" + SeasonEnum.SUMMER.name() + ",对应的索引为:" + SeasonEnum.SUMMER.ordinal());
        System.out.println("名字是:" + SeasonEnum.AUTUMN.name() + ",对应的索引为:" + SeasonEnum.AUTUMN.ordinal());
        System.out.println("名字是:" + SeasonEnum.WINTER.name() + ",对应的索引为:" + SeasonEnum.WINTER.ordinal());
    }
}

枚举值自定义构造函数

实际使用中,我们可能想给每个枚举值赋予更多的含义,例如,给每个季节一个中文说明和编码等。

因为最简单的枚举类型调用了默认的构造方法,如果我们要增加新的含义,则需要自己覆盖原来的构造方法。

package com.demo.enumDemo;

public enum SeasonEnum {

    SPRING("春天",01),SUMMER("夏天",02),AUTUMN("秋天",03),WINTER("冬天",04);

    private String name;
    private Integer code;

    SeasonEnum(String name, Integer code) {
        this.name = name;
        this.code = code;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }


    @Override
    public String toString() {
        return "SeasonEnum{" +
                "name='" + name + '\'' +
                ", code=" + code +
                '}';
    }
}

枚举类中定义抽象方法

既然编译器最终将每个枚举值声明为枚举类的实例,那我们能在枚举类中声明抽象方法让枚举值去实现么?

听起来有些不可思议,其实也是可以的。我们在枚举类Season中声明了一个抽象方法sayHello()。然后在创建枚举值时,就必须实现该抽象方法。最终的代码如下:

package com.demo.enumDemo;

public enum SeasonEnum {



    SPRING("春天",01){
        void sayHello(){
            System.out.println("hello 春天");
        }
    },
    SUMMER("夏天",02){
        @Override
        void sayHello() {
            System.out.println("hello 夏天");
        }
    },
    AUTUMN("秋天",03){
        @Override
        void sayHello() {
            System.out.println("hello 秋天");
        }
    },
    WINTER("冬天",04){
        @Override
        void sayHello() {
            System.out.println("hello 冬天");
        }
    };

    private String name;
    private Integer code;

    SeasonEnum(String name, Integer code) {
        this.name = name;
        this.code = code;
    }


    // 抽象方法必须定义在下面,否则会报错,原因未知
    abstract void sayHello();


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }


    @Override
    public String toString() {
        return "SeasonEnum{" +
                "name='" + name + '\'' +
                ", code=" + code +
                '}';
    }
}

枚举常用方法

枚举常见用法

向枚举中添加新方法

public enum Color {  
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
    // 成员变量  
    private String name;  
    private int index;  
    // 构造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
    // 普通方法  
    public static String getName(int index) {  
        for (Color c : Color.values()) {  
            if (c.getIndex() == index) {  
                return c.name;  
            }  
        }  
        return null;  
    }  
    // get set 方法  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public int getIndex() {  
        return index;  
    }  
    public void setIndex(int index) {  
        this.index = index;  
    }  
}

实现接口

public interface Behaviour {  
    void print();  
    String getInfo();  
}  
public enum Color implements Behaviour{  
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
    // 成员变量  
    private String name;  
    private int index;  
    // 构造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
//接口方法  
    @Override  
    public String getInfo() {  
        return this.name;  
    }  
    //接口方法  
    @Override  
    public void print() {  
        System.out.println(this.index+":"+this.name);  
    }  
}

使用接口组织枚举

public interface Food {  
    enum Coffee implements Food{  
        BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO  
    }  
    enum Dessert implements Food{  
        FRUIT, CAKE, GELATO  
    }  
}

switch

enum Signal {  
    GREEN, YELLOW, RED  
}  
public class TrafficLight {  
    Signal color = Signal.RED;  
    public void change() {  
        switch (color) {  
        case RED:  
            color = Signal.GREEN;  
            break;  
        case YELLOW:  
            color = Signal.RED;  
            break;  
        case GREEN:  
            color = Signal.YELLOW;  
            break;  
        }  
    }  
}

枚举和常量的区别

日常开发中,大家对这俩货的认识,其实都仅仅停留在“可穷尽状态集合”的层面。什么意思呢?就是当你发现某个字段存在有限的若干种状态时,你就下意识地选择使用常量类或者枚举。比如对于订单状态,你可以用枚举表示:

// 用枚举表示
package com.demo.enumDemo.EnumConstantCompare;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum OrderStatusEnum {

    UNPAY(1,"待付款"),
    PAYED(2,"代发货");

    // 省略其他状态

    private final Integer num;
    private final String desc;


}






// 也可以用常量表示
package com.demo.enumDemo.EnumConstantCompare;

public class OrderStatusConstant {

    public static int UNPAY = 1;
    public static int PAYED = 2;
}










// 有些人可能更骚一点
package com.demo.enumDemo.EnumConstantCompare;

public final class OrderConstant {

    // 订单状态
    public static class Status{
        public static int UNPAY = 1;
        public static int PAYED = 2;
    }



    // 订单类型
    public static class Type{
        public static int TB = 1;
        public static int JD = 1;
    }
}

但实际上,枚举和常量不是一个维度的东西,枚举是对象,常量是字段。常量能做的,枚举都能做,枚举能做的常量不一定能做。比如,枚举可以实现自定义的方法:

package com.demo.enumDemo.EnumConstantCompare;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.math.BigDecimal;

@Getter
@AllArgsConstructor
public enum MemberEnum {

    GOLD_MEMBER(1,"黄金会员"){
        @Override
        protected BigDecimal calculateFinalPrice(BigDecimal originPrice) {
            return originPrice.multiply(new BigDecimal("0.6"));
        }
    },
    SILVER_MEMBER(2,"白银会员"){
        @Override
        protected BigDecimal calculateFinalPrice(BigDecimal originPrice) {
            return originPrice.multiply(new BigDecimal("0.7"));
        }
    },
    BRONZE_MEMBER(3,"青铜会员"){
        @Override
        protected BigDecimal calculateFinalPrice(BigDecimal originPrice) {
            return originPrice.multiply(new BigDecimal("0.8"));
        }
    };

    private final Integer num;
    private final String name;





    /**
     * 定义抽象方法,留给子类实现
     * @param originPrice
     * @return
     */
    protected abstract BigDecimal calculateFinalPrice(BigDecimal originPrice);
}








package com.demo.enumDemo.EnumConstantCompare;

import java.math.BigDecimal;

public class TestEnumConstant {

    public static void main(String[] args) {

        BigDecimal originPrice = new BigDecimal("100");


        BigDecimal goldBigDecimal = MemberEnum.GOLD_MEMBER.calculateFinalPrice(originPrice);
        BigDecimal silverBigDecimal = MemberEnum.SILVER_MEMBER.calculateFinalPrice(originPrice);
        BigDecimal bronzeBigDecimal = MemberEnum.BRONZE_MEMBER.calculateFinalPrice(originPrice);


//        原价100,黄金会员打折后需要支付:60.0
//        原价100,白银会员打折后需要支付:70.0
//        原价100,青铜会员打折后需要支付:80.0
        System.out.println("原价" + originPrice + ",黄金会员打折后需要支付:" + goldBigDecimal);
        System.out.println("原价" + originPrice + ",白银会员打折后需要支付:" + silverBigDecimal);
        System.out.println("原价" + originPrice + ",青铜会员打折后需要支付:" + bronzeBigDecimal);
    }
}














// 如果用常量表示,就是下面这样
package com.demo.enumDemo.EnumConstantCompare;

public class Constants {

    // 黄金会员
    public static final Integer GOLD_MEMBER = 1;
    // 白银会员
    public static final Integer SILVER_MEMBER = 2;
    // 青铜会员
    public static final Integer BRONZE_MEMBER = 3;
}






package com.demo.enumDemo.EnumConstantCompare;

import java.math.BigDecimal;

public class MemberDemo {

    public static void main(String[] args) {
        BigDecimal bigDecimal = calculateFinalPrice(new BigDecimal("100"), Constants.GOLD_MEMBER);
        System.out.println(bigDecimal);
    }




    public static BigDecimal calculateFinalPrice(BigDecimal originPrice,Integer type){


        if (Constants.GOLD_MEMBER.equals(type)){
            return originPrice.multiply(new BigDecimal("0.6"));
        }else if (Constants.GOLD_MEMBER.equals(type)){
            return originPrice.multiply(new BigDecimal("0.7"));
        }else if (Constants.GOLD_MEMBER.equals(type)){
            return originPrice.multiply(new BigDecimal("0.8"));
        }else {
            return originPrice;
        }
    }
}

但上面计算价格的逻辑不能复用,需要计算会员价格时,就要拷贝一份上面的代码。当然,你可能会抽取到工具类中,但既然如此为什么不直接用枚举呢?枚举状态和计算方法放在一块,统一性更强、更合理。

这里并不是为了突出枚举优点而一味贬低常量类,只是想和大家说,枚举毕竟是对象,对象能玩的它都能玩,而常量类只能提供一个个字段变量,是很单薄的。

入参约束

对于常量,其实很难约束调用者按你的意图传参。比如上面计算会员价格:
你并不能约束我传 666,尽管666不代表任何一种会员类型

package com.demo.enumDemo.EnumConstantCompare;

import java.math.BigDecimal;

public class MemberDemo {

    public static void main(String[] args) {
    	// 我可能压根不知道你定义了Constants.GOLD_MEMBER,随便传666进去
        BigDecimal bigDecimal = calculateFinalPrice(new BigDecimal("100"), 666);
        System.out.println(bigDecimal);
    }




    public static BigDecimal calculateFinalPrice(BigDecimal originPrice,Integer type){


        if (Constants.GOLD_MEMBER.equals(type)){
            return originPrice.multiply(new BigDecimal("0.6"));
        }else if (Constants.GOLD_MEMBER.equals(type)){
            return originPrice.multiply(new BigDecimal("0.7"));
        }else if (Constants.GOLD_MEMBER.equals(type)){
            return originPrice.multiply(new BigDecimal("0.8"));
        }else {
            return originPrice;
        }
    }
}

但枚举不仅可以约束入参,还能很好的提示调用者:

下面这样写只是一个demo,实际肯定不这么写,

这里想表达的意思是:枚举就可以像类一样,可以限制入参的类型,像调用普通方法那样。

在这里插入图片描述

搭配switch,结构更清晰

这个理由其实是网上找的,switch和if else,我觉得半斤八两。

package com.demo.enumDemo.EnumConstantCompare;

import lombok.AllArgsConstructor;
import lombok.Getter;


public class EnumDemo {

    public static void main(String[] args) {

        // 这是苹果
        guessFruit(Fruit.APPLE);

    }

    public static void guessFruit(Fruit fruit){
        switch (fruit){
            case APPLE:
                System.out.println("这是苹果");
                break;
            case BANANA:
                System.out.println("这是香蕉");
                break;
            case ORANGE:
                System.out.println("这是橘子");
                break;
        }
    }
}


@AllArgsConstructor
@Getter
enum Fruit{

    APPLE(20200325,"中国山东",6.6),
    BANANA(20210325,"泰国曼谷",7.7),
    ORANGE(20220325,"日本北海道",8.8);

    private final Integer num;
    private final String origin;
    private final Double price;


}

什么时候用枚举、什么时候用常量?

最后说一下个人平时对枚举和常量类的取舍:

  • 错误码的定义用枚举
  • 字段状态用枚举或常量类都可以,个人更倾向于使用枚举,可玩性会比常量类高
  • 一些全局变量的定义可以使用常量类,比如USER_INFO_CACHE_KEY、SESSION_KEY

部分内容转载自:
https://zhuanlan.zhihu.com/p/64604609
https://www.jianshu.com/p/174467006572
https://www.zhihu.com/question/33659578

退税可以放弃,但补税不能放弃。

中国新闻网微博

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值