1

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>'
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
Victor Soares
  • 757
  • 1
  • 8
  • 34
  • 1
    Why do you expect the result of `filterGenericList` to be a `List`? Your method doesn't say that, it says it returns a list of _anything_. – Louis Wasserman May 12 '22 at 23:16
  • I know that. I'm trying to get a way to return a list of my object, using the interface to not create a lot of methods (one per class that implements the interface) – Victor Soares May 12 '22 at 23:18

3 Answers3

5

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());
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
1

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());
}
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
0

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

Martin
  • 56
  • 2