0

I have one overarching interface then a bunch of different child interfaces

public interface Parent {
  String foo(String s);
}

public interface ChildA extends Parents{
  default String foo(String s) { ... }
}

public interface ChildB extends Parents{
  default String foo(String s) { ... }
}

Then I have a bunch of spring components that implement these children

@Component
public class GenericChildA implements ChildA {
 ...
}

@Component
public class GenericChildB implements ChildB {
 ...
}

Finally I have another service that gets all of these child interface implementations through dependency injection. What I am trying to do is create a more generic function within this Composer class that takes in a List and calls their .foo() method

@Service
public class Composer {
  List<ChildA> childrenA;
  List<ChildB> childrenB;
  public Composer (List<ChildA> childrenA, List<ChildB> childrenB) {
    this.childrenA = childrenA
    this.childrenB = childrenB
  }

  public void baz() {
    bar(childrenA) // ERROR: childrenA is of type List<ChildrenA> not List<Parent>
    bar(childrenB) // ERROR: childrenB is of type List<ChildrenB> not List<Parent>
  }

  public void bar(List<Parent> parents) {
    parents.stream.forEach(parent::foo);
  }
}

Is there a way I can get this to work, I'd rather not write an maintain a function for each implementation of Parent?

0

2 Answers 2

2

To fix this I had to use a "Upper Bounded Wildcard" on the parameter of the function which fixed the typing issue. So instead of

public void bar(List<Parent> parents) {
  parents.stream.forEach(parent::foo);
}

We changed it to

public void bar(List<? extends Parent> parents) {
  parents.stream.forEach(parent::foo);
}
Sign up to request clarification or add additional context in comments.

Comments

1

The fundamental problem is that there is no inheritance relationship between List<ChildA> and List<Parent>. You mistakenly believe that ChildA inherits from Parent, so List<ChildA> inherits from List<Parent>. This is wrong.

List<ChildA> inherits from List<? extends Parent>.

If you understand list as "house", maybe you can figure this problem out:

public interface Parent { }

public interface Boys extends Parent{ }
public interface Girls extends Parent{ }

List<? extends Parent> aHouseForChildrenWhoExtendsParent = new List<Boys>(); // boys house.
List<? extends Parent> otherHouseForChildrenWhoExtendsParent = new List<Grils>(); // grils house.

List<Parent> parentHouse = new List<Boys>(); // ??? this is parent house, boys and girls can not go in here.

3 Comments

Any documentation about this concept of not actually inheriting? I know I have an answer now that works as I am expecting, but even when I used abstract classes instead of interfaces I still had the same issue. I thought classes would be a "true inheritance" relationship but I guess I am misunderstanding.
@ben8622 Of course, but I suggest you explore it. You can search for the following keywords and you will learn some interesting things: "array covariance", "contravariance", "generic invariance".

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.