1

I want to declare that a Generic Type is one of a subset of types, but I'm not sure how. Here is a simplified version of my situation:

Modules communicate with events. There are different types of events. Modules can consume different event types using different methods.

class EventProducerA implements Producer<EventTypeA>{
    @Override
    public EventTypeA produce_event(){
        return new EventTypeA(...);
    }
}

class EventProducerB implements Producer<EventTypeB>{
    @Override
    public EventTypeB produce_event(){
        return new EventTypeB(...);
    }
}


class EventConsumer{
    public void feed_event(EventTypeA ev){
        ...
    }

    public void feed_event(EventTypeB ev){
        ...
    }
}

Now, I have a main class where I link these modules. I WANT to have the code:

Producer<EventTypeA OR EventType B> producer = some_flag?new EventProducerA():new EventProducerB();
Consumer consumer = new EventConsumer()
consumer.feed_event(producer.produce_event());

Which would cause a compile error if some EventTypeC which is not handled by the consumer is produced by producer.

But unfortunately there is no OR option for Generic Types in Java.

So what's the best thing to do? Am I forced to use ugly casting solutions?

Peter
  • 12,274
  • 9
  • 71
  • 86

3 Answers3

4

Not even nasty casting solutions would work, even if you tried them, because if EventConsumer has different overloads for different event types, then you have to know at compile time which overload is chosen in which code path. Do the awkward but straightforward thing:

if (some_flag) {
   Producer<EventTypeA> producer = new EventProducerA();
   Consumer consumer = new EventConsumer();
   consumer.feed_event(producer.produce_event());
} else {
   Producer<EventTypeB> producer = new EventProducerB();
   Consumer consumer = new EventConsumer();
   consumer.feed_event(producer.produce_event());
}

Don't try to get fancy.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
1

Unfortunately what you ask cannot be done. If you think about it you would remove the static information about the type produced by produce_event, preventing the compiler from knowing what overload of feed_event it should use.

As alternative solutions you can keep it simple, adding a bit of boilerplate code:

if(some_flag)
{
    EventProducerA producer = new EventProducerA();
    Consumer consumer = new EventConsumer();
    consumer.feed_event(producer.produce_event());
}
else
{
    EventProducerA producer = new EventProducerA();
    Consumer consumer = new EventConsumer();
    consumer.feed_event(producer.produce_event());
}

If you don't like this solution you need to find a way to get double dispatch.

interface IProducer
{
   void consume_event(Consumer consumer);
}

abstract class Producer<T> : IProducer
{
}

class EventProducerA implements Producer<EventTypeA>{
    @Override
    public EventTypeA produce_event(){
        return new EventTypeA(...);
    }

    @Override void consume(Consumer consumer)
    {
        consumer.feed_event(produce_event());    
    }
}

class EventConsumer{
    public void feed_event(IProducer p)
    {
        p.consume(this);
    }
    public void feed_event(EventTypeA ev){
        ...
    }

    public void feed_event(EventTypeB ev){
        ...
    }
}

Finally, you might want to have a look at trying to use the equivalent of the Either algebraic data type that you can find in functional programming languages. This Q&A shows how you could simulate it in Java.

Community
  • 1
  • 1
mariosangiorgio
  • 5,520
  • 4
  • 32
  • 46
1

There is always way,

first create common interface for both your events

interface EventType {
        void accept(EventConsumer consumer);
}

now implementation

class EventTypeA implements EventType {
        @Override
        public void accept(EventConsumer consumer) {
            consumer.feed_event(this);
        }
    }

and how to use it? simple

Producer<? extends EventType> producer = condition ? new EventProducerA() : new EventProducerB();
EventConsumer consumer = new EventConsumer();
EventType eventType= producer.produce_event();
eventType.accept(consumer);
user902383
  • 8,420
  • 8
  • 43
  • 63
  • Ah. This is nice, except that the set of possible consumers is ever-expanding and there're relatively few types of events. But, I could put the the accept method in a base-class of all events, and have ``` @Override public void accept(Consumer consumer) { consumer.feed_event(this); } ```. Only I now require some way to get the Type of the class inheriting this method, which I think is also not implemented in Java! – Peter Feb 02 '16 at 23:08
  • @Peter still it not an issue, you can pass base class of consumer, or you could even create separate interface for that (and if you are using java 8, then you could provide default implementation) it might be nasty, but all what you need to remember will be to add new interface to each `Consumer` – user902383 Feb 02 '16 at 23:54