首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用枚举代替Java中的常用类

用枚举代替Java中的常用类
EN

Stack Overflow用户
提问于 2013-06-07 03:06:00
回答 3查看 3.4K关注 0票数 5

在处理一个项目时,我接到一项任务,要设计一组类,这些类实现一个定义简单操作的接口。通常这些类会以特定的顺序一次完成它们的工作,但只从其中一个调用方法的可能性也是一个要求。

考虑到上述所有情况,并考虑到:-每个类都有非常基本的逻辑-不需要扩展另一个类-将所有类放在一个文件中可能更方便-在需要时编辑源文件不是问题

我想出了下面的解决方案(实际的类并不是人为设计的,但下面的例子足以给你一些基本的想法):

代码语言:javascript
复制
public enum Bestiary {
DOG(1) {
    @Override
    void makeNoise(Loudspeaker ls) {
        ls.shoutOutLoud("I am alpha dog");
    }
},

CAT(2) {
    @Override
    void makeNoise(Loudspeaker ls) {
        ls.shoutOutLoud("I am beta cat");
    }
},

RAT(3) {
    List<String> foods = new ArrayList<>();
    {
        foods.add("gods");
        foods.add("dogs");
        foods.add("cats");
        foods.add("other rats");
    }

    @Override
    void makeNoise(Loudspeaker ls) {
        StringBuilder cry = new StringBuilder("I am THE rat; usually I eat ");
        for (int i = 0; i < foods.size(); i++) {
            cry.append(foods.get(i));
            if (i != (foods.size() - 1)) {
                cry.append(", ");
            }
        }
        ls.shoutOutLoud(cry.toString());
    }
},

THE_THING(4) {
    String name = "r2d2";

    @Override
    void makeNoise(Loudspeaker ls) {
        ls.shoutOutLoud(calculateHash(name));

    }

    private String calculateHash(String smth) {
        return String.valueOf(smth.hashCode());
    }
};

private int id;

public int getId() {
    return id;
}

Bestiary(int id) {
    this.id = id;
}

abstract void makeNoise(Loudspeaker ls); // all enum elements will need to implement this - kind of like implementing an interface (which was also an option); note that we pass some arbitrary object and call methods on it
}

调用这个类的代码可能如下所示:

代码语言:javascript
复制
public final class Loudspeaker {
private static Loudspeaker loudspeaker = new Loudspeaker();

public static void shoutOutLoud(String cry) {
    System.out.println(cry);
}

static class Noizemakers {
    public static void makeSomeNoise() {
        for (Bestiary creature: Bestiary.values()) {
            System.out.println(creature + " with id " + creature.getId() +  " says: ");
            creature.makeNoise(loudspeaker);
        }
    }
}

public static void main(String[] args) {
    Noizemakers.makeSomeNoise();
    Bestiary.CAT.makeNoise(loudspeaker);
}
}

在一次代码审查中,我的建议被嘲笑为“太老套了,利用了枚举有类主体和方法的事实,总体上有一种糟糕的代码味道”。虽然将它转换成一个单独的接口、一堆常用的Java类等只需要几分钟的时间,但我对这种解释并不是很满意。有没有什么指导方针说你应该只使用基本形式的枚举,就像其他语言一样?这种方法有什么真正的缺点?Joshua Bloch建议将单例编写为枚举-在这种情况下,这样的枚举必须是一个完整的类,对吧?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-06-07 05:11:09

可以在任何具有浅层类层次结构的地方使用enum,在可扩展性(类具有更多,枚举具有更少)和简洁性(如果功能简单,枚举可能更清晰)之间进行权衡。这并不是一直做的正确的事情,但偶尔做是可以的,只是要意识到其中的差异,我在下面列出了其中的一些。

在我看来,您正在处理的情况似乎正是语言设计者通过允许枚举具有方法来支持的那类事情。在我看来,你丝毫没有颠覆语言特性的意图。

作为我工作中的一个示例,我经常使用带有方法的枚举作为实现各种无状态策略的一种方式,但也将它们用于其他用途,包括作为Class的一种可扩展形式。

回答您的特定问题:

这种方法有什么真正的缺点?

与接口+具体类方法相比:

在特定枚举值中定义的

  • 方法不能从该值外部调用。例如,如果你为RAT定义了一个名为枚举的方法,没有人可以调用它。枚举没有可变的状态,因为每个枚举值实际上都是一个枚举枚举类文件,如果类型的数量急剧增加,或者每个类型的子类枚举值的代码变得过长,它们实际上是final
  • No squeak()
  • ...

有没有什么指导方针说你应该只使用基本形式的枚举,就像其他语言一样?

我从来没见过。

Joshua Bloch建议将单例编写为枚举-在这种情况下,这样的枚举必须是一个完整的类,对吧?

按照你的提问者的逻辑,是的。因此,这就成了一个问题,你更愿意听他们的话,还是听乔什·布洛赫的话。

票数 3
EN

Stack Overflow用户

发布于 2013-06-07 03:17:07

您应该只在没有(或很少)可能添加新元素的情况下使用enum。这并不是说你不应该给出一个类似枚举类的函数。For example

代码语言:javascript
复制
public enum Planet {
    MERCURY (3.303e+23, 2.4397e6),
    VENUS   (4.869e+24, 6.0518e6),
    EARTH   (5.976e+24, 6.37814e6),
    MARS    (6.421e+23, 3.3972e6),
    JUPITER (1.9e+27,   7.1492e7),
    SATURN  (5.688e+26, 6.0268e7),
    URANUS  (8.686e+25, 2.5559e7),
    NEPTUNE (1.024e+26, 2.4746e7);

    private final double mass;   // in kilograms
    private final double radius; // in meters
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
}

造成这种情况的原因有多种:

根据word.

  • Compatibility.的定义,如果我想把一只鸟添加到你的bestiary中该怎么办?对于非枚举的枚举没有任何意义。你必须修改enum。这很简单,但是如果有些用户使用旧版本的枚举,而另一些用户使用更高版本的枚举,该怎么办?这会产生很多兼容性问题。

如果必须使用枚举,一种(次优)解决方案是:

代码语言:javascript
复制
interface Animal {
    void makeNoise();
}

enum Bestiary implements Animal {
    // the rest of the stuff here
}

然后,当前接受Bestiary的任何方法都可以很容易地切换为接受Animal。然而,如果你这样做了,最好还是拥有:

代码语言:javascript
复制
interface Animal {
    void makeNoise();
}
public class Dog implements Animal {...}
public class Cat implements Animal {...}
public class Rat implements Animal {...}
public class Thing implements Animal {...}
票数 3
EN

Stack Overflow用户

发布于 2013-06-07 03:11:43

我个人的观点是,enum不应该包含任何变异方法,因为它违反了枚举值具有恒定状态的大多数假设。...But再看一遍你的作品,实际上并不是这样的。这样做看起来确实很奇怪,但这更像是一种“意想不到的用法”,而不是一种明确的“错误的方式”。

只需确保枚举类型中任何可能可修改的值都不能从外部访问,比如foods。( Strings可以设为final,所以这不是问题,但设为foods final并不能阻止人们操作列表本身,只是分配一个新的列表。)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16970190

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档