Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 883
Benjamin Avatar asked Jan 22 '21 23:01

Benjamin


People also ask

How many interfaces are there in Collection framework?

Each of the six core collection interfaces — Collection, Set, List, Map, SortedSet, and SortedMap — has one static factory method.

What are the implementation classes for the list interface?

The implementation classes of the List interface are ArrayList, LinkedList, Stack, and Vector. ArrayList and LinkedList are widely used in Java programming.

Can multiple classes implement one interface in the same program?

Yes multiple classes can implement one interface irrespective of being in the same program or different. There is no such restriction. Interface only defines the common methods that should be included in the Implementation classes.


1 Answers

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();
    });
like image 63
Pshemo Avatar answered Oct 25 '22 14:10

Pshemo