Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
148 views
in Technique[技术] by (71.8m points)

java - How to create a collection/list of instances of many classes which implement two common interfaces (without modifying those classes)?

I have two interfaces (IfaceA, IfaceB) and two classes implementing those interface (class C, class D):

interface IfaceA {
    void doA();
}

interface IFaceB {
    void doB();
}

class C implements IfaceA, IFaceB {
    public void doA() {}
    public void doB() {}
}

class D implements IfaceA, IFaceB {
    public void doA() {}
    public void doB() {}
}

I cannot change the signature of those classes.

How can I make a list or collection of instances of classes that implement both interfaces?

What I tried:

public static void main(String[] args) {

    List<? extends IfaceA & IFaceB> test_1;
    List<? extends IfaceA, IFaceB> test_2;

    Class<? extends IfaceA, IFaceB>[] test_3;
}

are all wrong (a wildcard can have only one bound while I'm not sure whether it's possible with type bound).

I know this one might work:

Object[] objects = new Object[] {
        new C(), new D()
};

for (Object o: objects) {
    IfaceA a = (IfaceA) o;
    IfaceB b = (IfaceB) o;

    a.doA();
    b.doB();
}

but this simply doesn't look right.

question from:https://stackoverflow.com/questions/65854342/how-to-create-a-collection-list-of-instances-of-many-classes-which-implement-two

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Possible way around is creating wrapper type which

  • can only wrap instances of classes which implements IfaceA, IFaceB
  • allows calling all methods from both interfaces on wrapped instance on wrapped instance.

It can look like:

class Wrapper<T extends IfaceA & IFaceB> implements IfaceA, IFaceB {

    private final T element;

    public Wrapper(T element) {
        this.element = element;
    }

    @Override
    public void doA() {
        element.doA();
    }

    @Override
    public void doB() {
        element.doB();
    }

}

This will let us use that Wrapper as type of elements in the List:

class Demo {

    public static void main(String[] args) {

        //Wrapper<?> can represent both Wrapper<C> and Wrapper<D>
        List<Wrapper<?>> list = new ArrayList<>();
        list.add(new Wrapper<>(new C())); 
        list.add(new Wrapper<>(new D()));

        for (Wrapper<?> wrapper : list){
            wrapper.doA(); //both calls compile fine
            wrapper.doB(); //both calls compile fine
        }

    }

}

Alternative version.

Instead of delegating method calls to wrapped element we can access that element via getter and call all methods from IfaceA & IFaceB interfaces directly on it.

class Wrapper<T extends IfaceA & IFaceB> {
    private final T element;

    public Wrapper(T element) {
        this.element = element;
    }

    public T getElement() {
        return element;
    }
}

public class Demo {
    public static void main(String[] args) {
        List<Wrapper<?>> list = new ArrayList<>();
        list.add(new Wrapper<>(new C()));
        list.add(new Wrapper<>(new D()));

        for (Wrapper<?> wrapper : list){
            //here `var` represents "some" subtype of both IfaceA & IFaceB 
            var element = wrapper.getElement();             

            // so following is legal
            element.doA(); 
            element.doB(); 
        }
    }
}

OR if someone prefers Java 8 style we can rewrite above loop like

list.stream()
    .map(Wrapper::getElement)
    .forEach(element -> {
        element.doA();
        element.doB();
    });

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...