如何使一个 Java 类实现一个具有两个泛型类型的接口?

我有一个通用接口

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

我有一个类,它使用两种类型的对象,所以我想这样做:

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

显然我做不到。

我当然可以亲自执行调度,例如。

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

但是我正在寻找泛型提供的编译时类型检查和分派解决方案。

我能想到的最好的解决方案是定义单独的接口,例如。

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

从功能上来说,我认为这个解决方案还可以,只是冗长而丑陋。

有什么想法吗?

64149 次浏览

由于类型擦除,不能两次实现相同的接口(使用不同的类型参数)。

至少,您可以通过执行以下操作对调度的实现进行一些小的改进:

public class TwoTypesConsumer implements Consumer<Fruit> {

果实是番茄和苹果的祖先。

考虑封装:

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) {  .....  }
}
}

如果创建这些静态内部类让您感到困扰,可以使用匿名类:

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

以下是基于 Steve McLeod 就是其中之一的一种可能的解决方案:

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

问题的隐含要求是共享状态的 Consumer<Tomato>Consumer<Apple>对象。对 Consumer<Tomato>, Consumer<Apple>对象的需求来自于期望这些对象作为参数的其他方法。为了共享状态,我需要一个实现它们的类。

Steve 的想法是使用两个内部类,每个类实现一个不同的泛型类型。

这个版本为实现 Consumer 接口的对象添加了 getter,然后可以将这些接口传递给需要它们的其他方法。

只是偶然发现了这个。我碰巧遇到了同样的问题,但我用另一种方式解决了: 我刚刚创建了一个这样的新界面

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

不幸的是,这被认为是作为 Consumer<A>,而不是作为 Consumer<B>对所有逻辑。因此,您必须为类中的第二个使用者创建一个小的 Adapter,如下所示

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){ //...
}
}

如果需要 Consumer<A>,只需传递 this,如果需要 Consumer<B>,只需传递 consumerAdapter

您不能在一个类中直接执行此操作,因为由于擦除了泛型类型和重复的接口声明,下面的类定义无法编译。

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

将相同的消费操作封装在一个类中的任何其他解决方案都需要将类定义为:

class TwoTypesConsumer { ... }

这是没有意义的,因为您需要重复/复制两个操作的定义,它们不会从接口引用。恕我直言,这样做是一个不好的小和代码复制,我试图避免。

这也可能是一个指示器,表明在一个类中有太多的责任来消耗2个不同的对象(如果它们不耦合的话)。

然而,我正在做的和您可以做的是添加显式的工厂对象,以下面的方式创建连接的消费者:

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

如果实际上这些类型真的是耦合的(相关的) ,那么我建议以这样的方式创建一个实现:

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

优点是工厂类知道这两个实现,有一个共享状态(如果需要) ,如果需要,您可以返回更多耦合的使用者。没有不是从接口派生的重复使用方法声明。

请注意,如果每个消费者不是完全相关的,它们可能是独立的(仍然是私有的)类。

这种解决方案的缺点是类复杂度较高(即使这可以是一个 Java 文件) ,要访问使用方法,您需要再调用一次,因此不需要:

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

你有:

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

总而言之,您可以使用2个内部类在一个顶级类中使用 定义2通用消费者,但是在调用的情况下,您需要首先获得对适当的 执行消费者的引用,因为它不能仅仅是一个消费者对象。

另一种避免使用更多类的方法(例如使用 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);
}

很抱歉回答老问题,但是 我真的很喜欢!试试这个选项:

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

我想这就是你要找的。

你会得到这样的输出:

西红柿吃完了!

我吃苹果

绳子断了!

在 Functionalstyle 中,在不实现接口的情况下这样做很容易,而且它还可以进行编译时类型检查。

使用实体的函数接口

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

我们的经理适当地处理和使用实体

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