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
'programing' 카테고리의 다른 글
Vue-cli: git bash 창에서 새 프로젝트를 만드는 동안 화살표 키가 작동하지 않습니다. (0) | 2022.09.03 |
---|---|
Storybook의 셋업 기능을 통한 Vuex 사용 방법 (0) | 2022.09.01 |
Python을 머신 코드로 컴파일 할 수 있습니까? (0) | 2022.09.01 |
무엇 `created()`의 Vue.js 작문 api에 있는 가치야? (0) | 2022.09.01 |
Vue.js에서 어레이 속성의 서브섹션 페이지 매기는 방법 (0) | 2022.09.01 |