简体   繁体   中英

Java Generic (1 of N) Types

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?

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.

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 .

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM