javagenericsunchecked-cast

Java generic method: return specific type (without unchecked cast warning)


Given a collection c with elements of type T (or T's subtypes) return some element e of type R from c so that R is a subtype of T.

Motivation: I want to use specific methods of R on e.

UPDATE: From the aioobe's answer added to the requirements acceptance of R's subtypes for e.

Here is my working implementation:

import java.util.ArrayList;
import java.util.List;

public class ReturnSpecificTypeFromGeneric {

    public static <T, R extends T> R findFirstElementOfType(
            List<? extends T> elements,
            Class<R> requiredClass
    ) {
        for (T element : elements) {
            if (requiredClass.isAssignableFrom(element.getClass())) {
                // WARNING: Unchecked cast: 'T' to 'R'
                //noinspection unchecked
                return (R) element; // [1]
            }
        }
        return null;
    }

    public static void main(String[] args) {
        List<Mammal> mammals = new ArrayList<>();
        mammals.add(new Elephant());
        mammals.add(new WhiteRhino());
        mammals.add(new Rhino());
        Rhino rhino = findFirstElementOfType(mammals, Rhino.class);
        System.out.println(rhino.getClass()); // returns: class WhiteRhino
    }
}

class Mammal {}
class Elephant extends Mammal {}
class Rhino extends Mammal {}
class WhiteRhino extends Rhino {}

Questions:

  1. Is this a right approach?
  2. Can the unchecked cast (marked as [1]) be avoided, and how, and should it be cared about?
  3. Other solutions if possible.

Solution

    1. That code looks reasonable to me.

    2. You could "hide" the cast by instead using requiredClass.cast(element).

    3. Basically the same solution, but you could express it using streams as follows:

      public static <T, R extends T> R findFirstElementOfType(
              List<? extends T> elements,
              Class<R> requiredClass
      ) {
          return elements.stream()
                  .filter(requiredClass::isInstance)
                  .findFirst()
                  .map(requiredClass::cast)
                  .orElseThrow(NoSuchElementException::new);
      }
      

      or .orElse(null) in the end, if you want the exact behavior of your original snippet.