'Unable to assign the result returned by the Method that receives a generic List and returns a List<?>

I'm trying to filter a List of objects that implements an interface. And I'm trying to create a generic method for all the classes.

Something like:

interface SomeInterface {
    String getFlag();
}


class SomeObject implements SomeInterface {
    public String getFlag() {
        return "X";
    }
}


List<SomeObject> someObjectList = new ArrayList<>();

// Compilation error here
List<SomeObject> filterList = filterGenericList(someObjectList, "X");


private List<?> filterGenericList(List<? extends SomeInterface> objects, String flag) {
        return objects.stream()
                .filter(it -> it.getFlag().equals(flag))
                .collect(Collectors.toList());
}

How do I run away from the compilation Error?

 Incompatible types. 
 Found: 'java.util.List<capture<?>>', 
 Required: 'java.util.List<SomeObject>'


Solution 1:[1]

When you return List<?> that means the method returns a list of some unknown type. Java doesn't know that it's the same type of list as the input list. To signal that, create a generic type T and have both the input and output lists be List<T>.

private <T extends SomeInterface> List<T> filterGenericList(List<T> objects, String flag) {
    return objects.stream()
        .filter(it -> it.getFlag().equals(flag))
        .collect(Collectors.toList());
}

Solution 2:[2]

Unbounded wildcard <?> also called the unknown type is a way to tell the compiler that a generic type can't be predicted, and therefore the compiler will disallow to assign a list of unknown type to a list of SomeObject because it's impossible to ensure that this assignment is safe.

If you have a list of objects that belong to different classes implementing SomeInterface in order to distinguish between them, you can explicitly pass an instance of target class Class<T> into a method as an argument.

Seems like you're trying to reinvent the same mechanism by utilizing method getFlag(). It doesn't look reliable because nothing can stop you from making a typo or accidentally implementing getFlag() in two different classes in such a way that it'll return the same value.

private <T extends SomeInterface> List<T> filterGenericList(List<? extends SomeInterface> objects, 
                                                            Class<T> targetClass) {
    return objects.stream()
        .filter(it -> targetClass.isAssignableFrom(it.getClass()))
        .map(targetClass::cast)
        .collect(Collectors.toList());
}

Solution 3:[3]

first of all List<?> doesn't have any type information. You expect a List of SomeObject but your method just returns a List of any type.

To achieve your goal you should define your filter method as

private static  <T extends SomeInterface> List<T> filterGenericList(List<T> objects, String flag)

In this way you guarantee that the returned list is of the same type like the input list

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 John Kugelman
Solution 2
Solution 3 Martin