Search code examples
javainheritancesonarqube

Properly return list of class or of subclass


My situation is rather simple, I have a class Parent and a class Child which is extending Parent.

If I have a method getStuff that can return either an instance of Parent or of Child, I can simply do that:

public Parent getStuff(boolean p) {
  return p ? new Parent() : new Child();
}

Now what if my method can return either List<Parent> or List<Child>? I have to do that:

public List<? extends Parent> getListOfStuff(boolean p) {
  return p
    ? myOtherMethodThatReturnsListOfParents()
    : myOtherMethodThatReturnsListOfChilds();
}

public List<Parent> myOtherMethodThatReturnsListOfParents() {
  return new ArrayList<Parent>();
}

public List<Child> myOtherMethodThatReturnsListOfChilds(boolean p) {
  return new ArrayList<Child>();
}

But when I do that, Sonar reports "Generic wildcard types should not be used in return types" as a critical problem.

What is the proper way of fixing it?

Thanks

EDIT: the cleanest solution I've seen is ChristianRein's solution below (rebuild the List<Child> as a List<Parent>, it does exactly what I want and is so obvious in insight):

public List<Parent> getListOfStuff(boolean p) {
  return p
    ? myOtherMethodThatReturnsListOfParents()
    : new ArrayList<Parent>(myOtherMethodThatReturnsListOfChilds());
}

Solution

  • As your class Child extends your class Parent, you can easily write

    public class Parent {
    
        public List<Parent> getListOfStuff(final boolean p) {
            return p
                    ? myOtherMethodThatReturnsListOfParents()
                    : new ArrayList<>(myOtherMethodThatReturnsListOfChildren());
        }
    
        private List<Child> myOtherMethodThatReturnsListOfChildren() {
            return List.of(new Child());
        }
    
        private List<Parent> myOtherMethodThatReturnsListOfParents() {
            return List.of(new Parent());
        }
    
    }
    

    This way, Sonar accepts it.

    In the calling method, you may use instanceof to decide if you recieved a Child or a Parent object. In JUnit5, you may use assertInstanceOf:

    @Test
    void parents() {
        // when
        final List<Parent> result = new Parent().getListOfStuff(true);
    
        // then
        assertInstanceOf(Parent.class, result.get(0));
        assertInstanceOf(Child.class, result.get(0)); // fails
    }
    
    @Test
    void children() {
        // when
        final List<Parent> result = new Parent().getListOfStuff(false);
    
        // then
        assertInstanceOf(Child.class, result.get(0));
        assertInstanceOf(Parent.class, result.get(0));
    }