Enforce class fields to be same generic type without specifying a class type parameter

hendalst :

Consider an object which produces data that is consumed by another object to generate a result. The process is encapsulated in a class and the intermediate data is not relevant.

In the example below, the process takes place on construction and there is no issue. The type parameter on the constructor ensures compatible consumer/producers.

public class ProduceAndConsume {
    public interface Producer<T> {
        T produce();
    }
    public interface Consumer<V> {
        void consume(V data);
    }

    public <IntermediateType> ProduceAndConsume(Producer<? extends IntermediateType> producer, Consumer<IntermediateType> consumer) {
        consumer.consume(producer.produce());
    }

    ...

}

If I wish to store references to the producer/consumer and do the processing later, then the code becomes:

public class ProduceAndConsume<IntermediateType> {
    public interface Producer<T> {
        T produce();
    }
    public interface Consumer<V> {
        void consume(V data);
    }

    private Producer<? extends IntermediateType> producer;
    private Consumer<IntermediateType> consumer;

    public ProduceAndConsume(Producer<? extends IntermediateType> producer, Consumer<IntermediateType> consumer) {
        this.producer = producer;
        this.consumer = consumer;
    }

    ...

    private void doLater() {
        consumer.consume(producer.produce());
    }
 }

This introduces a generic type parameter to the class and forces it to be specified in any implementation.

My question is, is there any way to avoid this? The intermediate data type is not produced, stored, or consumed by this class and is not relevant to the user of the class. The compiler has all the information it requires to enforce type consistency, assuming the IntermediateType type param can be specified somewhere.

Note that this is a simplified example and the actual class runs the processing asynchronously and periodically some time after construction.

Andy Turner :

Store the producer and consumer in an inner class with the type variable:

public class ProduceAndConsume {
  private class Inner<IntermediateType> {
    private Producer<? extends IntermediateType> producer;
    private Consumer<IntermediateType> consumer;

    // Constructor omitted.

    private void doLater() {
      consumer.consume(producer.produce());
    }
  }

  private final Inner<?> inner;

  public <IntermediateType> ProduceAndConsume(Producer<? extends IntermediateType> producer, Consumer<IntermediateType> consumer) {
    this.inner = new Inner<>(producer, consumer);
  }

  private void doLater() { // Or just invoke inner.doLater() directly.
    inner.doLater();  
  }
}

In this way, you enforce that the producer and consumer are related for when you need to use them later, but you don't need that type information afterwards in the ProduceAndConsume instance.

Producer<String> stringProducer = ...;
Consumer<Object> objConsumer = ...;

// No externally-visible type variables.
ProduceAndConsume pac1 = new ProduceAndConsume(stringProducer, objConsumer);

But the compiler enforces compatibility of the producer and consumer:

Consumer<Integer> integerConsumer = ...;
// Compiler error.
ProduceAndConsume pac2 = new ProduceAndConsume(stringProducer, integerConsumer);

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=444952&siteId=1