programing

2개의 범용 타입을 가진 하나의 인터페이스를 구현하는 Java 클래스를 만드는 방법은 무엇입니까?

prostudy 2022. 9. 1. 21:28
반응형

2개의 범용 타입을 가진 하나의 인터페이스를 구현하는 Java 클래스를 만드는 방법은 무엇입니까?

범용 인터페이스가 있다

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

2종류의 오브젝트를 소비하는 클래스가 있기 때문에 다음과 같은 작업을 하고 싶습니다.

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

기능적으로는 이 솔루션은 괜찮다고 생각합니다.그것은 단지 장황하고 추하다.

좋은 생각 있어요?

캡슐화를 검토합니다.

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의 아이디어는 각각 다른 일반 유형을 구현하는 두 개의 내부 클래스를 사용하는 것이었습니다.

이 버전에서는 컨슈머인터페이스를 구현하는 객체에 getter를 추가하여 이를 예상되는 다른 메서드로 전달할 수 있습니다.

적어도 다음과 같은 방법으로 디스패치 구현을 약간 개선할 수 있습니다.

public class TwoTypesConsumer implements Consumer<Fruit> {

토마토와 사과의 조상이 되는 과일.

이걸 우연히 발견했어.같은 문제가 발생했지만 다른 방법으로 해결했습니다.이렇게 새로운 인터페이스를 만들었습니다.

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

은 is스럽게 unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun unfortun 라고 생각됩니다.Consumer<A>서 and and and and and and and 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){ //...
    }
}

그렇다면Consumer<A>에는 그냥 .this및 if , 「」Consumer<B>만 하면 됩니다.consumerAdapter

Functional style에서는 인터페이스를 구현하지 않고 컴파일 시간 유형 검사를 수행할 수 있습니다.

엔티티를 소비하기 위한 델의 기능 인터페이스

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

범용 유형의 삭제 및 중복된 인터페이스 선언으로 인해 다음 클래스 정의를 컴파일할 수 없기 때문에 이 작업을 한 클래스에서 직접 수행할 수 없습니다.

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

한 클래스에서 동일한 사용 작업을 패킹하기 위한 다른 솔루션에서는 클래스를 다음과 같이 정의해야 합니다.

class TwoTypesConsumer { ... }

양쪽 조작의 정의를 반복/복원해야 하고 인터페이스에서 참조되지 않기 때문에 의미가 없습니다.IMHO가 이것을 하는 것은 내가 피하고 싶은 나쁜 작은 코드 복제입니다.

이는 또한 하나의 클래스에서 두 개의 다른 개체를 소비하기 위한 책임이 너무 크다는 것을 나타내는 지표일 수 있습니다(연결되지 않은 경우).

하지만 제가 하고 있는 것과 여러분이 할 수 있는 것은 다음과 같은 방법으로 연결된 소비자를 만들기 위해 명시적인 공장 객체를 추가하는 것입니다.

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

장점은 공장 클래스가 두 가지 구현을 모두 알고 있으며 공유 상태(필요한 경우)가 있으며 필요에 따라 더 결합된 소비자를 반환할 수 있다는 것입니다.인터페이스에서 파생되지 않은 반복 소비 방식 선언은 없습니다.

각 소비자는 완전히 관련이 없는 경우 독립(아직도 개인) 클래스일 수 있습니다.

이 솔루션의 단점은 더 높은 클래스의 복잡성입니다(이것이 1개의 Java 파일일 수도 있습니다).사용 방법에 액세스하려면 다음 대신1개의 콜이 더 필요합니다.

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

다음과 같은 것이 있습니다.

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

요약하면, 2개의 내부 클래스를 사용하여 1개의 최상위 클래스에 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));
  }
}

그게 당신이 찾고 있는 거라고 생각해요.

다음의 출력이 표시됩니다.

토마토 소비량!

나는 사과를 먹는다.

사용된 문자열!

언급URL : https://stackoverflow.com/questions/1297972/how-to-make-a-java-class-that-implements-one-interface-with-two-generic-types

반응형