7

I was looking at the example implementation of Publisher (AsyncIterablePublisher.java) of the reactive-streams spec when I stumbled into something that I don't understand why it was done that way.

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

Being realistic that I'm not such an advanced programmer as the person that wrote this I'm sure that there is a reason to do it that way. But I'm also not able to explain why it would be better than doing this (which is how I would have done it).

enum Signal {
  Cancel,
  Subscribe,
  Send;
}

Could someone explain it to me why it would be better? Advantages / disadvantages?

Juru
  • 1,623
  • 17
  • 43
  • 1
    Well, you can see there that there is a non-enum class there that also implements `Signal`, so a `Signal` enum is out of the question. Why the three were not placed under a single enum type that implements `Signal` is a different question. – RealSkeptic May 11 '15 at 07:05

5 Answers5

2

Do not be too strict, here is my interpretation of this code. Let us call the owner of reactive-streams Roland.

At first Roland needs a Common Interface for all inboundSignals

static interface Signal {};

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

Signals like Cancel, Subscribe and Send have allways the same purpose are immutable and occur very frequently, so it is good Idea to implements they as Joshua Bloch's Singleton:

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

the other way to do the same is similar to your proposal and my favorite:

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
}

As you can see, this is a different implementation. But the idea is the same - Signal as singleton

We move on and find this code:

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

Since inboundSignals can contain multiple Request objects it is not possible to implement this type of Signal as Singleton. Therefore it can not be a member of CommonSignals or implemented as enum.


Conclusion

Roland used one of many possibilities to implement a singletons. I think that's more a matter of taste how to do it.

Community
  • 1
  • 1
alex
  • 8,904
  • 6
  • 49
  • 75
1

For the type of usage in AsyncIterablePublisher the two forms are equivalent, and arguably the latter, one enum with multiple constants, is more natural.

Actually, the former form is very rare. I can see one argument in favor of using it (but being so rare it means that this point is usually not that important): when you define each constant in its own enum you get a chance to define different methods/fields, such as:

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

so you can now do Send.Instance.someSendSpecificMethod(). Very awkward, very rare.

Itay Maman
  • 30,277
  • 10
  • 88
  • 118
1

The difference is that you can add another instances of Signal without changing the original enum. Your code can work with Signal instances and different types can be supplied. The same as interface for classes - gives you flexibility but it is useful only if you need it. In your case I don't see much use because the interface is empty and the enums implementing it have nothing in them.

You can check this site for a good example of enum with interfaces:

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

Veselin Davidov
  • 7,031
  • 1
  • 15
  • 23
0

So since I didn't know which of the answers to approve I mailed the author of the piece of code and asked him about what he thought of it. Here is the conversation (block's are from him. Normal text is mine):

Hi Michael,

What is the difference with having just one enum then? You would not have three singletons but still three uniquely defined objects.:

Sure, that could be done, but then I'd have to invent a name for it (like ConcreteSignal), my chosen encoding avoids that :)

enum Signal {
  Cancel, Subscribe, Send;
}

I think that is also thread-safe and if you really want to have the shared interface, that you can share with objects for instance, you could do it like this:

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

You could definitely do as you have done above, however I'd argue that they [Cancel, Subscribe, Send] are not more concrete than Request (which is also a signal): https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/examples/src/main/java/org/reactivestreams/example/unicast/AsyncIterablePublisher.java#L49

Also, that could also encourage to use the methods of Enum to enumerate all possible Signals, which it couldn't, since Request is not a Signal. Makes sense?

Indeed in Scala you would rather do it with an Object but in Java it is very rare to see something like your implementation. I was just curious that I was missing some neat feature in Java. But if I'm understanding correctly it is because you are more confortable with that implementation as it is closer to Scala, your preferred language?

I guess there's a bit of that to it, but the singleton-as-enum-pattern in Java is quite well-known, so the only "weird" thing would be to have a distinct "enum" for each of Cancel, Subscribe and Send, but as I explained previously, since Request cannot be encoded like that I opted for the more "Scala-y" version.

So if "Request" would not need any parameters, I'd done as you suggested: enum Signal { Cancel, Request, Subscribe, Send }

Mystery solved :-).

Juru
  • 1,623
  • 17
  • 43
0

what I think that the only reason for using interface with enum, that some of signals have data diffrent than other signals so he needs to extends the enum type to attach data to it.

Bassem Reda Zohdy
  • 12,662
  • 3
  • 33
  • 39