6

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.

Benjamin
  • 3,217
  • 2
  • 27
  • 42

1 Answers1

7

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();
    });
Pshemo
  • 122,468
  • 25
  • 185
  • 269
  • The alternative solution is quite impressive. Nevertheless I think it's a big limitation of the wildcard bound that it cannot extend two interfaces. Thank you, good advices! – Benjamin Jan 23 '21 at 17:28
  • @Benjamin You are welcome. Also agreed, don't know why we can't have ` extends Type1 & Type2>`. Based on accepted answer in [Why can't you have multiple interfaces in a bounded wildcard generic?](https://stackoverflow.com/q/6643241) it looks like there ware some preparations to allow it. My guess is that since this is not that very common use-case/need (which can be solved by creating another interface which will extend both IfaceA & IFaceB which will allow us to have `List extends mixOfAB>`) Java developers probably focused on other things. – Pshemo Jan 23 '21 at 17:50