How to make a Java class that implements one interface with two generic types?

JavaGenericsInterfaceMultiple Inheritance

Java Problem Overview


I have a generic interface

public interface Consumer<E> {
    public void consume(E e);
}

I have a class that consumes two types of objects, so I would like to do something like:

public class TwoTypesConsumer implements Consumer<Tomato>, Consumer<Apple>
{
   public void consume(Tomato t) {  .....  }
   public void consume(Apple a) { ...... }
}

Apparently I can't do that.

I can of course implement the dispatch myself, e.g.

public class TwoTypesConsumer implements Consumer<Object> {
   public void consume(Object o) {
      if (o instanceof Tomato) { ..... }
      else if (o instanceof Apple) { ..... }
      else { throw new IllegalArgumentException(...) }
   }
}

But I am looking for the compile-time type-checking and dispatching solution that generics provide.

The best solution I can think of is to define separate interfaces, e.g.

public interface AppleConsumer {
   public void consume(Apple a);
}

Functionally, this solution is OK, I think. It's just verbose and ugly.

Any ideas?

Java Solutions


Solution 1 - Java

Consider encapsulation:

public class TwoTypesConsumer {
    private TomatoConsumer tomatoConsumer = new TomatoConsumer();
    private AppleConsumer appleConsumer = new AppleConsumer();
    
    public void consume(Tomato t) { 
        tomatoConsumer.consume(t);
    }
    
    public void consume(Apple a) { 
        appleConsumer.consume(a);
    }

    public static class TomatoConsumer implements Consumer<Tomato> {
        public void consume(Tomato t) {  .....  }
    }

    public static class AppleConsumer implements Consumer<Apple> {
        public void consume(Apple a) {  .....  }
    }
}

If creating these static inner classes bothers you, you can use anonymous classes:

public class TwoTypesConsumer {
    private Consumer<Tomato> tomatoConsumer = new Consumer<Tomato>() {
        public void consume(Tomato t) {
        }
    };
    
    private Consumer<Apple> appleConsumer = new Consumer<Apple>() {
        public void consume(Apple a) {
        }
    };

    public void consume(Tomato t) {
        tomatoConsumer.consume(t);
    }

    public void consume(Apple a) {
        appleConsumer.consume(a);
    }
}

Solution 2 - Java

Because of type erasure you can't implement the same interface twice (with different type parameters).

Solution 3 - Java

Here's a possible solution based on Steve McLeod's one:

public class TwoTypesConsumer {
	public void consumeTomato(Tomato t) {...}
	public void consumeApple(Apple a) {...}

	public Consumer<Tomato> getTomatoConsumer() {
		return new Consumer<Tomato>() {
			public void consume(Tomato t) {
				consumeTomato(t);
			}
		}
	}

	public Consumer<Apple> getAppleConsumer() {
		return new Consumer<Apple>() {
			public void consume(Apple a) {
				consumeApple(t);
			}
		}
	}
}

The implicit requirement of the question was Consumer<Tomato> and Consumer<Apple> objects that share state. The need for Consumer<Tomato>, Consumer<Apple> objects comes from other methods that expect these as parameters. I need one class the implement them both in order to share state.

Steve's idea was to use two inner classes, each implementing a different generic type.

This version adds getters for the objects that implement the Consumer interface, which can then be passed to other methods expecting them.

Solution 4 - Java

At least, you can make a small improvement to your implementation of dispatch by doing something like the following:

public class TwoTypesConsumer implements Consumer<Fruit> {

Fruit being an ancestor of Tomato and Apple.

Solution 5 - Java

just Stumbled upon this. It just happened, that I had the same Problem, but I solved it in a different way: I just created a new Interface like this

public interface TwoTypesConsumer<A,B> extends Consumer<A>{
    public void consume(B b);
}

unfortunately, this is considered as Consumer<A> and NOT as Consumer<B> against all Logic. So you have to create a small Adapter for the second consumer like this inside your class

public class ConsumeHandler implements TwoTypeConsumer<A,B>{
  
    private final Consumer<B> consumerAdapter = new Consumer<B>(){
        public void consume(B b){
            ConsumeHandler.this.consume(B b);
        }
    };

    public void consume(A a){ //...
    }
    public void conusme(B b){ //...
    }
}

if a Consumer<A> is needed, you can simply pass this, and if Consumer<B> is needed just pass consumerAdapter

Solution 6 - Java

In Functional style it is quite easy do this without implementing the interface and also it does the compile time type checking.

Our functional interface to consume entity

@FunctionalInterface
public interface Consumer<E> { 
     void consume(E e); 
}

our manager to process and consume entity appropriately

public class Manager {
    public <E> void process(Consumer<E> consumer, E entity) {
        consumer.consume(entity);
    }

    public void consume(Tomato t) {
        // Consume Tomato
    }

    public void consume(Apple a) {
        // Consume Apple
    }

    public void test() {
        process(this::consume, new Tomato());
        process(this::consume, new Apple());
    }
}

Solution 7 - Java

You cannot do this directly in one class as the class definition below cannot be compiled due to erasure of generic types and duplicate interface declaration.

class TwoTypesConsumer implements Consumer<Apple>, Consumer<Tomato> { 
 // cannot compile
 ...
}

Any other solution for packing the same consume operations in one class requires to define your class as:

class TwoTypesConsumer { ... }

which is pointless as you need to repeat/duplicate the definition of both operations and they won't be referenced from interface. IMHO doing this is a bad small and code duplication which I'm trying to avoid.

This might be an indicator also that there is too much responsibility in one class to consume 2 different objects (if they aren't coupled).

However what I'm doing and what you can do is to add explicit factory object to create connected consumers in the following way:

interface ConsumerFactory {
     Consumer<Apple> createAppleConsumer();
     Consumer<Tomato> createTomatoConsumer();
}

If in reality those types are really coupled (related) then I would recommend to create an implementation in such way:

class TwoTypesConsumerFactory {

    // shared objects goes here

    private class TomatoConsumer implements Consumer<Tomato> {
        public void consume(Tomato tomato) {
            // you can access shared objects here
        }
    }

    private class AppleConsumer implements Consumer<Apple> {
        public void consume(Apple apple) {
            // you can access shared objects here
        }
    }


    // It is really important to return generic Consumer<Apple> here
    // instead of AppleConsumer. The classes should be rather private.
    public Consumer<Apple> createAppleConsumer() {
        return new AppleConsumer();
    }

    // ...and the same here
    public Consumer<Tomato> createTomatoConsumer() {
        return new TomatoConsumer();
    }
}

The advantage is that the factory class knows both implementations, there is a shared state (if needed) and you can return more coupled consumers if needed. There is no repeating consume method declaration which aren't derived from interface.

Please note that each consumer might be independent (still private) class if they aren't completely related.

The downside of that solution is a higher class complexity (even if this can be a one java file) and to access consume method you need one more call so instead of:

twoTypesConsumer.consume(apple)
twoTypesConsumer.consume(tomato)

you have:

twoTypesConsumerFactory.createAppleConsumer().consume(apple);
twoTypesConsumerFactory.createTomatoConsumer().consume(tomato);

To summarize you can define 2 generic consumers in one top-level class using 2 inner classes but in case of calling you need to get first a reference to appropriate implementing consumer as this cannot be simply one consumer object.

Solution 8 - Java

Another alternative to avoid the use of more classes. (example using java8+)

// Mappable.java
public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

// TwoMappables.java
public interface TwoMappables {
    default Mappable<A> mapableA() {
         return new MappableA();
    }

    default Mappable<B> mapableB() {
         return new MappableB();
    }

    class MappableA implements Mappable<A> {}
    class MappableB implements Mappable<B> {}
}

// Something.java
public class Something implements TwoMappables {
    // ... business logic ...
    mapableA().mapTo(A);
    mapableB().mapTo(B);
}

Solution 9 - Java

Sorry for answer old questions, but I really love it! Try this option:

public class MegaConsumer implements Consumer<Object> {

  Map<Class, Consumer> consumersMap = new HashMap<>();
  Consumer<Object> baseConsumer = getConsumerFor(Object.class);

  public static void main(String[] args) {
    MegaConsumer megaConsumer = new MegaConsumer();
    
    //You can load your customed consumers
    megaConsumer.loadConsumerInMapFor(Tomato.class);
    megaConsumer.consumersMap.put(Apple.class, new Consumer<Apple>() {
        @Override
        public void consume(Apple e) {
            System.out.println("I eat an " + e.getClass().getSimpleName());
        }
    });
    
    //You can consume whatever
    megaConsumer.consume(new Tomato());
    megaConsumer.consume(new Apple());
    megaConsumer.consume("Other class");
  }

  @Override
  public void consume(Object e) {
    Consumer consumer = consumersMap.get(e.getClass());
    if(consumer == null) // No custom consumer found
      consumer = baseConsumer;// Consuming with the default Consumer<Object>
    consumer.consume(e);
  }

  private static <T> Consumer<T> getConsumerFor(Class<T> someClass){
    return t -> System.out.println(t.getClass().getSimpleName() + " consumed!");
  }

  private <T> Consumer<T> loadConsumerInMapFor(Class<T> someClass){
    return consumersMap.put(someClass, getConsumerFor(someClass));
  }
}

I think that is what you are looking for.

You get this output:

> Tomato consumed! > > I eat an Apple > > String consumed!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondaphshezView Question on Stackoverflow
Solution 1 - JavaSteve McLeodView Answer on Stackoverflow
Solution 2 - JavaShimi BandielView Answer on Stackoverflow
Solution 3 - JavadaphshezView Answer on Stackoverflow
Solution 4 - JavaBuhbView Answer on Stackoverflow
Solution 5 - JavaRafael TView Answer on Stackoverflow
Solution 6 - Javamano_kspView Answer on Stackoverflow
Solution 7 - JavakitarekView Answer on Stackoverflow
Solution 8 - JavafingerprintsView Answer on Stackoverflow
Solution 9 - JavaAwes0meM4nView Answer on Stackoverflow