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; }