Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need to filter a list to a particular subclass using generics

I have a List that contains a certain superclass (like Vehicle), and I would like to write a method that returns the objects in that list that are instances of a certain subclass (like Car).

So far I have this, but it generates a typical "unchecked" operation compiler warning:

public <T extends Vehicle> List<T> getVehiclesOfType(Class<T> type) {
    List<T> result = new ArrayList<T>();

    for (Vehicle vehicle : getVehicles()) {
        if (type.isAssignableFrom(vehicle.getClass())) {
            result.add(type.cast(vehicle)); // Compiler warning here
            // Note, (T)vehicle generates an "Unchecked cast" warning (IDE can see this one)
        }
    }

    return result;
}

Warning: Note: Test.java uses unchecked or unsafe operations.

I'm ok with any other method of accomplishing this (I couldn't find anything in Collections, but it's possible some JDK method can do it), but ideally it would provide the following interface:

List<Car> cars = getVehiclesOfType(Car.class);

I would like to know why I was receiving a compiler warning on the original code, though.

like image 887
Nicole Avatar asked Jan 05 '11 15:01

Nicole


2 Answers

You're getting a warning because there's no way for the compiler (or the IDE) to know that the cast is safe, without understanding the meaning of isAssignableFrom(). But isAssignableFrom() isn't a language feature, it's just a library method. As far as the compiler's concerned, it's the same as if you'd said

    if (type.getName().contains("Elvis")) {
        result.add(type.cast(vehicle));
    }

However, you know what isAssignableFrom() means, so you know it's safe. This is exactly the sort of situation @SuppressWarnings is meant for.

like image 72
David Moles Avatar answered Sep 19 '22 21:09

David Moles


How about this?

if (type.isInstance(vehicle)) {
    result.add((T)(vehicle));
}

Does the compiler still complain like that?


But if I were you I'd use Guava, that will make your method a one-liner:

public <T extends Vehicle> List<T> getVehiclesOfType(Class<T> type) {
    return Lists.newArrayList(Iterables.filter(getVehicles(), type));
}
like image 24
Sean Patrick Floyd Avatar answered Sep 19 '22 21:09

Sean Patrick Floyd