【问题标题】:Multiple enums vs One enum多个枚举与一个枚举
【发布时间】:2015-05-11 06:56:39
【问题描述】:

我正在查看响应式流规范的 Publisher (AsyncIterablePublisher.java) 的示例实现,但偶然发现了一些我不明白为什么要这样做的东西。

static interface Signal {};
enum Cancel implements Signal { Instance; };
enum Subscribe implements Signal { Instance; };
enum Send implements Signal { Instance; };

现实一点,我不像写这篇文章的人那样高级程序员,我确信这样做是有理由的。但我也无法解释为什么它会比这样做更好(我会这样做)。

enum Signal {
  Cancel,
  Subscribe,
  Send;
}

有人可以向我解释一下为什么它会更好吗?优点/缺点?

【问题讨论】:

  • 嗯,你可以看到那里有一个非枚举类也实现了Signal,所以Signal 枚举是不可能的。为什么这三个没有放在实现 Signal 的单个枚举类型下是另一个问题。

标签: java enums reactive-streams


【解决方案1】:

不要太严格,这是我对这段代码的解释。让我们称反应流的所有者为 Roland。

起初,Roland 需要一个通用接口供所有inboundSignals 使用

static interface Signal {};

ConcurrentLinkedQueue<Signal> inboundSignals = new ConcurrentLinkedQueue<Signal>();

CancelSubscribeSend 这样的信号总是具有相同的目的并且是不可变的并且非常频繁地发生,因此最好将它们实现为Joshua Bloch's Singleton

enum Cancel    implements Signal { Instance; };
enum Subscribe implements Signal { Instance; };
enum Send      implements Signal { Instance; };

做同样的事情的另一种方式类似于你的建议和我最喜欢的:

enum CommonSignals implements Signal{

    Cancel {
        @Override
        void debug() {
            System.out.println("Cancel");
        }
    },

    Subscribe {
        @Override
        void debug() {
            System.out.println("Subscribe");
        }
    },

    Send {
        @Override
        void debug() {
            System.out.println("Send");
        }
    };

    abstract void debug();

    [...] some other methods I could need in the future
}

如您所见,这是一个不同的实现。但是思路是一样的——Signal as singleton

我们继续寻找这段代码:

static final class Request implements Signal {
    final long n;
    Request(final long n) { // every Request has different value of n
        this.n = n;
    }
};

由于inboundSignals 可以包含多个Request 对象,因此不可能将这种类型的信号实现为单例。因此它不能是CommonSignals 的成员或实现为enum


结论

Roland 使用了多种可能性之一来实现单例。我认为这更多的是一个品味问题。

【讨论】:

  • 所有答案都令人满意,所以对所有人都 +1。
【解决方案2】:

对于 AsyncIterablePublisher 中的使用类型,这两种形式是等价的,可以说后者,一个具有多个常量的枚举更自然。

其实前一种形式非常少见。我可以看到一个支持使用它的论点(但如此罕见,这意味着这一点通常并不那么重要):当您在自己的枚举中定义每个常量时,您就有机会定义不同的方法/字段,例如:

enum Cancel implements Signal { Instance; };
enum Send implements Signal { 
  Instance; 
  public void someSendSpecificMethod() { ... }
}

所以你现在可以做Send.Instance.someSendSpecificMethod()。非常尴尬,非常罕见。

【讨论】:

    【解决方案3】:

    不同之处在于您可以在不更改原始枚举的情况下添加另一个 Signal 实例。您的代码可以使用 Signal 实例,并且可以提供不同的类型。与类的接口相同 - 为您提供灵活性,但仅在您需要时才有用。在您的情况下,我看不到太多用处,因为接口是空的,并且实现它的枚举中没有任何内容。

    你可以在这个网站上查看一个很好的带有接口的枚举示例:

    http://www.selikoff.net/2011/06/15/java-enums-can-implement-interfaces/

    【讨论】:

      【解决方案4】:

      因此,由于我不知道哪些答案可以批准,我将这段代码的作者邮寄给了他,并询问了他对此的看法。这是对话(块来自他。普通文本是我的):

      嗨,迈克尔,

      那么只有一个枚举有什么区别?你不会有三个单例,但仍然有三个唯一定义的对象。:

      当然可以,但是我必须为它起一个名字 (如 ConcreteSignal),我选择的编码避免了这种情况:)

      enum Signal {
        Cancel, Subscribe, Send;
      }
      

      我认为这也是线程安全的,如果您真的想要共享接口,例如可以与对象共享,您可以这样做:

      enum ConcreteSignal implements Signal {
        Cancel,
        Subscribe,
        Send;
      }
      

      你绝对可以像上面那样做,但我认为 他们 [取消、订阅、发送] 并不比请求更具体 (这也是一个信号): https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/examples/src/main/java/org/reactivestreams/example/unicast/AsyncIterablePublisher.java#L49

      另外,这也可以鼓励使用 Enum 的方法来 枚举所有可能的信号,它不能,因为 Request 是 不是信号。有意义吗?

      确实,在 Scala 中,您更愿意使用 Object 来执行此操作,但在 Java 中,很少看到类似您的实现的东西。我只是好奇我错过了 Java 中的一些简洁功能。但是,如果我理解正确,那是因为您更喜欢该实现,因为它更接近您首选的语言 Scala?

      我想这有点像,但是单例作为枚举模式 在 Java 中是非常有名的,所以唯一“奇怪”的事情就是 每个取消、订阅和发送都有一个不同的“枚举”,但作为 我之前解释过,因为请求不能像我这样编码 选择了更“Scala-y”的版本。

      所以如果“请求”不需要任何参数,我会像你一样做 建议:枚举信号 { 取消、请求、订阅、发送 }

      谜团解开了:-)。

      【讨论】:

        【解决方案5】:

        我认为使用枚举接口的唯一原因是,某些信号的数据与其他信号不同,因此他需要扩展枚举类型以将数据附加到它。

        【讨论】:

          猜你喜欢
          • 2010-09-11
          • 1970-01-01
          • 2012-11-17
          • 1970-01-01
          • 2014-10-09
          • 1970-01-01
          • 2014-09-21
          • 1970-01-01
          相关资源
          最近更新 更多