如何制作一个用两种泛型类型实现一个接口的 Java 类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1297972/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 07:57:57  来源:igfitidea点击:

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

javagenericsinterfacemultiple-inheritance

提问by daphshez

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?

有任何想法吗?

采纳答案by Steve McLeod

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

回答by Shimi Bandiel

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

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

回答by Buhb

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.

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

回答by daphshez

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

这是基于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);
            }
        }
    }
}

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.

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

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

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

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

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

回答by Rafael T

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

不幸的是,这被认为是Consumer<A>而不是Consumer<B>反对所有的逻辑。所以你必须在你的班级中为第二个消费者创建一个小的适配器

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

如果Consumer<A>需要 a ,你可以简单地 pass this,如果Consumer<B>需要,只需 passconsumerAdapter

回答by kitarek

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

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

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:

该解决方案的缺点是类的复杂性更高(即使这可以是一个 java 文件)并且要访问消耗方法,您需要再调用一次,而不是:

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

you have:

你有:

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

To summarize you can define2 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 implementingconsumer as this cannot be simply one consumer object.

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

回答by winter

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

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

回答by Awes0meM4n

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!

番茄吃完了!

我吃一个苹果

字符串消耗!

回答by mano_ksp

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