用好 Java 中的枚举,让你的工作效率飞起来!

下面,让我们扩展上面的示例,实现从比萨的一个阶段到另一个阶段的过渡,并了解如何摆脱之前使用的if语句和switch语句:

publicclass Pizza {

private PizzaStatus status;

publicenum PizzaStatus {

ORDERED (5){

@Override

public boolean isOrdered() {

returntrue;

}

},

READY (2){

@Override

public boolean isReady() {

returntrue;

}

},

DELIVERED (0){

@Override

public boolean isDelivered() {

returntrue;

}

};

privateint timeToDelivery;

public boolean isOrdered() {returnfalse;}

public boolean isReady() {returnfalse;}

public boolean isDelivered(){returnfalse;}

public int getTimeToDelivery() {

return timeToDelivery;

}

PizzaStatus (int timeToDelivery) {

this.timeToDelivery = timeToDelivery;

}

}

public boolean isDeliverable() {

returnthis.status.isReady();

}

public void printTimeToDeliver() {

System.out.println("Time to delivery is " +

this.getStatus().getTimeToDelivery());

}

// Methods that set and get the status variable.

}

下面这段代码展示它是如何 work 的:

@Test

public void givenPizaOrder_whenReady_thenDeliverable() {

Pizza testPz = new Pizza();

testPz.setStatus(Pizza.PizzaStatus.READY);

assertTrue(testPz.isDeliverable());

}

6.EnumSet and EnumMap


6.1. EnumSet

EnumSet 是一种专门为枚举类型所设计的 Set 类型。

HashSet相比,由于使用了内部位向量表示,因此它是特定 Enum 常量集的非常有效且紧凑的表示形式。

它提供了类型安全的替代方法,以替代传统的基于int的“位标志”,使我们能够编写更易读和易于维护的简洁代码。

EnumSet 是抽象类,其有两个实现:RegularEnumSetJumboEnumSet,选择哪一个取决于实例化时枚举中常量的数量。

在很多场景中的枚举常量集合操作(如:取子集、增加、删除、containsAllremoveAll批操作)使用EnumSet非常合适;如果需要迭代所有可能的常量则使用Enum.values()

publicclass Pizza {

privatestatic EnumSet undeliveredPizzaStatuses =

EnumSet.of(PizzaStatus.ORDERED, PizzaStatus.READY);

private PizzaStatus status;

publicenum PizzaStatus {

}

public boolean isDeliverable() {

returnthis.status.isReady();

}

public void printTimeToDeliver() {

System.out.println("Time to delivery is " +

this.getStatus().getTimeToDelivery() + " days");

}

public static List getAllUndeliveredPizzas(List input) {

return input.stream().filter(

(s) -> undeliveredPizzaStatuses.contains(s.getStatus()))

.collect(Collectors.toList());

}

public void deliver() {

if (isDeliverable()) {

PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy()

.deliver(this);

this.setStatus(PizzaStatus.DELIVERED);

}

}

// Methods that set and get the status variable.

}

下面的测试演示了展示了 EnumSet 在某些场景下的强大功能:

@Test

public void givenPizaOrders_whenRetrievingUnDeliveredPzs_thenCorrectlyRetrieved() {

List pzList = new ArrayList<>();

Pizza pz1 = new Pizza();

pz1.setStatus(Pizza.PizzaStatus.DELIVERED);

Pizza pz2 = new Pizza();

pz2.setStatus(Pizza.PizzaStatus.ORDERED);

Pizza pz3 = new Pizza();

pz3.setStatus(Pizza.PizzaStatus.ORDERED);

Pizza pz4 = new Pizza();

pz4.setStatus(Pizza.PizzaStatus.READY);

pzList.add(pz1);

pzList.add(pz2);

pzList.add(pz3);

pzList.add(pz4);

List undeliveredPzs = Pizza.getAllUndeliveredPizzas(pzList);

assertTrue(undeliveredPzs.size() == 3);

}

6.2. EnumMap

EnumMap是一个专门化的映射实现,用于将枚举常量用作键。与对应的 HashMap 相比,它是一个高效紧凑的实现,并且在内部表示为一个数组:

EnumMap<Pizza.PizzaStatus, Pizza> map;

让我们快速看一个真实的示例,该示例演示如何在实践中使用它:

publicstatic EnumMap<PizzaStatus, List>

groupPizzaByStatus(List pizzaList) {

EnumMap<PizzaStatus, List> pzByStatus =

new EnumMap<PizzaStatus, List>(PizzaStatus.class);

for (Pizza pz : pizzaList) {

PizzaStatus status = pz.getStatus();

if (pzByStatus.containsKey(status)) {

pzByStatus.get(status).add(pz);

} else {

List newPzList = new ArrayList();

newPzList.add(pz);

pzByStatus.put(status, newPzList);

}

}

return pzByStatus;

}

下面的测试演示了展示了 EnumMap 在某些场景下的强大功能:

@Test

public void givenPizaOrders_whenGroupByStatusCalled_thenCorrectlyGrouped() {

List pzList = new ArrayList<>();

Pizza pz1 = new Pizza();

pz1.setStatus(Pizza.PizzaStatus.DELIVERED);

Pizza pz2 = new Pizza();

pz2.setStatus(Pizza.PizzaStatus.ORDERED);

Pizza pz3 = new Pizza();

pz3.setStatus(Pizza.PizzaStatus.ORDERED);

Pizza pz4 = new Pizza();

pz4.setStatus(Pizza.PizzaStatus.READY);

pzList.add(pz1);

pzList.add(pz2);

pzList.add(pz3);

pzList.add(pz4);

EnumMap<Pizza.PizzaStatus,List> map = Pizza.groupPizzaByStatus(pzList);

assertTrue(map.get(Pizza.PizzaStatus.DELIVERED).size() == 1);

assertTrue(map.get(Pizza.PizzaStatus.ORDERED).size() == 2);

assertTrue(map.get(Pizza.PizzaStatus.READY).size() == 1);

}

7. 通过枚举实现一些设计模式


7.1 单例模式

通常,使用类实现 Singleton 模式并非易事,枚举提供了一种实现单例的简便方法。

《Effective Java 》和《Java与模式》都非常推荐这种方式,使用这种方式方式实现枚举可以有什么好处呢?

《Effective Java》

这种方法在功能上与公有域方法相近,但是它更加简洁,无偿提供了序列化机制,绝对防止多次实例化,即使是在面对复杂序列化或者反射攻击的时候。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现 Singleton的最佳方法。—-《Effective Java 中文版 第二版》

《Java与模式》

《Java与模式》中,作者这样写道,使用枚举来实现单实例控制会更加简洁,而且无偿地提供了序列化机制,并由JVM从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式。

下面的代码段显示了如何使用枚举实现单例模式:

publicenum PizzaDeliverySystemConfiguration {

INSTANCE;

PizzaDeliverySystemConfiguration() {

// Initialization configuration which involves

// overriding defaults like delivery strategy

}

private PizzaDeliveryStrategy deliveryStrategy = PizzaDeliveryStrategy.NORMAL;

public static PizzaDeliverySystemConfiguration getInstance() {

return INSTANCE;

}

public PizzaDeliveryStrategy getDeliveryStrategy() {

return deliveryStrategy;

}

}

如何使用呢?请看下面的代码:

PizzaDeliveryStrategy deliveryStrategy = PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy();

通过 PizzaDeliverySystemConfiguration.getInstance() 获取的就是单例的 PizzaDeliverySystemConfiguration

7.2 策略模式

通常,策略模式由不同类实现同一个接口来实现的。

这也就意味着添加新策略意味着添加新的实现类。使用枚举,可以轻松完成此任务,添加新的实现意味着只定义具有某个实现的另一个实例。

下面的代码段显示了如何使用枚举实现策略模式:

publicenum PizzaDeliveryStrategy {

EXPRESS {

@Override

public void deliver(Pizza pz) {

System.out.println(“Pizza will be delivered in express mode”);

}

},

NORMAL {

@Override

public void deliver(Pizza pz) {

System.out.println(“Pizza will be delivered in normal mode”);

}

};

public abstract void deliver(Pizza pz);

}

Pizza增加下面的方法:

public void deliver() {

if (isDeliverable()) {

PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy()

.deliver(this);

this.setStatus(PizzaStatus.DELIVERED);

}

}

如何使用呢?请看下面的代码:

@Test

public void givenPizaOrder_whenDelivered_thenPizzaGetsDeliveredAndStatusChanges() {

Pizza pz = new Pizza();

pz.setStatus(Pizza.PizzaStatus.READY);

pz.deliver();

assertTrue(pz.getStatus() == Pizza.PizzaStatus.DELIVERED);

}

8. Java 8 与枚举


Pizza 类可以用Java 8重写,您可以看到方法 lambda 和Stream API如何使 getAllUndeliveredPizzas()groupPizzaByStatus()方法变得如此简洁:

getAllUndeliveredPizzas():

public static List getAllUndeliveredPizzas(List input) {

return input.stream().filter(

(s) -> !deliveredPizzaStatuses.contains(s.getStatus()))

.collect(Collectors.toList());

}

groupPizzaByStatus() :

publicstatic EnumMap<PizzaStatus, List>

groupPizzaByStatus(List pzList) {

EnumMap<PizzaStatus, List> map = pzList.stream().collect(

Collectors.groupingBy(Pizza::getStatus,

() -> new EnumMap<>(PizzaStatus.class), Collectors.toList()));

return map;

}

9. Enum 类型的 JSON 表现形式


使用Jackson库,可以将枚举类型的JSON表示为POJO。下面的代码段显示了可以用于同一目的的Jackson批注:

@JsonFormat(shape = JsonFormat.Shape.OBJECT)

publicenum PizzaStatus {

ORDERED (5){

@Override

public boolean isOrdered() {

returntrue;

}

},

READY (2){

@Override

public boolean isReady() {

returntrue;

}

},

DELIVERED (0){

@Override

public boolean isDelivered() {

returntrue;

}

};

privateint timeToDelivery;

public boolean isOrdered() {returnfalse;}

public boolean isReady() {returnfalse;}

public boolean isDelivered(){returnfalse;}

@JsonProperty(“timeToDelivery”)

public int getTimeToDelivery() {

return timeToDelivery;

}

private PizzaStatus (int timeToDelivery) {

this.timeToDelivery = timeToDelivery;

}

}

我们可以按如下方式使用 PizzaPizzaStatus

Pizza pz = new Pizza();

pz.setStatus(Pizza.PizzaStatus.READY);

System.out.println(Pizza.getJsonString(pz));

生成 Pizza 状态以以下JSON展示:

{

“status” : {

“timeToDelivery” : 2,

“ready” : true,

“ordered” : false,

“delivered” : false

},

“deliverable” : true

}

有关枚举类型的JSON序列化/反序列化(包括自定义)的更多信息,请参阅Jackson-将枚举序列化为JSON对象。

10.总结


本文我们讨论了Java枚举类型,从基础知识到高级应用以及实际应用场景,让我们感受到枚举的强大功能。

总结

这份面试题几乎包含了他在一年内遇到的所有面试题以及答案,甚至包括面试中的细节对话以及语录,可谓是细节到极致,甚至简历优化和怎么投简历更容易得到面试机会也包括在内!也包括教你怎么去获得一些大厂,比如阿里,腾讯的内推名额!

某位名人说过成功是靠99%的汗水和1%的机遇得到的,而你想获得那1%的机遇你首先就得付出99%的汗水!你只有朝着你的目标一步一步坚持不懈的走下去你才能有机会获得成功!

成功只会留给那些有准备的人!

int timeToDelivery) {

this.timeToDelivery = timeToDelivery;

}

}

我们可以按如下方式使用 PizzaPizzaStatus

Pizza pz = new Pizza();

pz.setStatus(Pizza.PizzaStatus.READY);

System.out.println(Pizza.getJsonString(pz));

生成 Pizza 状态以以下JSON展示:

{

“status” : {

“timeToDelivery” : 2,

“ready” : true,

“ordered” : false,

“delivered” : false

},

“deliverable” : true

}

有关枚举类型的JSON序列化/反序列化(包括自定义)的更多信息,请参阅Jackson-将枚举序列化为JSON对象。

10.总结


本文我们讨论了Java枚举类型,从基础知识到高级应用以及实际应用场景,让我们感受到枚举的强大功能。

总结

这份面试题几乎包含了他在一年内遇到的所有面试题以及答案,甚至包括面试中的细节对话以及语录,可谓是细节到极致,甚至简历优化和怎么投简历更容易得到面试机会也包括在内!也包括教你怎么去获得一些大厂,比如阿里,腾讯的内推名额!

某位名人说过成功是靠99%的汗水和1%的机遇得到的,而你想获得那1%的机遇你首先就得付出99%的汗水!你只有朝着你的目标一步一步坚持不懈的走下去你才能有机会获得成功!

成功只会留给那些有准备的人!

[外链图片转存中…(img-SZ1XibH6-1721194780066)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值