Skip to content
Advertisement

Stream return class type generic

I have a list like this

public static ImmutableList<House> HOUSES =
    ImmutableList.of(
        new House.WithName("first"),
        new House.WithName("second"),
        new House.WithoutPlace("third", 9400));

and i have a method to find a house by its name, but i want it return the class type not the house interface, example when i do findHouse("third") i want it to return House.WithoutPlace not House, how i can do that?

    public static <T extends House> ImmutableList<T> findHouse(String name) {
    return HOUSES.stream()
        .filter(h -> h.name().equals(name))
        .collect(toImmutableList()); 
// returns no instance(s) of type variable(s) exist
}

Advertisement

Answer

You cannot do this at all, unless you know, which type of house you are expecting at a specific location in the code.

The modified method will give you a single subtype instance of House, assuming, that you can provide the class type of that house.

@SuppressWarnings("unchecked")
public static <T extends House> T findHouse(Class<T> type, String name) {
    for (House house : HOUSES) {
        if (type.isInstance(house) && house.name.equals(name)) {
            return (T) house;
        }
    }
    return null;
}

The problem with your example is, that at the time of search, you cannot be sure, which instance you will get (and of what subtype it is). The compiler cannot know at compile time, whether you will get a House.WithName or a House.WithoutPlace. Therefore it cannot deduce, what kind of list to return, and must return a list of House. You must later cast individually to handle the different subtypes, by checking the instance at the time, when you extract the instance from the resulting list:

// your orifinal findHouse
List<House> housesWithMyName = findHouse("myName");
for (House house : housesWithMyName) {
    if (house instanceof House.WithName) {
        House.WithName myHood = (House.WithName) house;
        // ...do something with myHood.
    }
}

Alternatively you can use the modified version, but it will only ever return one instance matching both name and expected type at most, or null if no such house exists.

Ultimately, you could also use this version, in which the result is still a List (with generic element type T), which will only contain any houses matching both type and name. You can now be sure, that you get only any House.WithName or House.WithoutPlace etc.

@SuppressWarnings("unchecked")
public static <T extends House> List<T> findHouse(Class<T> type, String name) {
    List<T> result = new ArrayList<>();
    for (House house : HOUSES) {
        if (type.isInstance(house) && house.name.equals(name)) {
            result.add((T) house);
        }
    }
    return result;
}
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement