scottb
scottb

Reputation: 10084

Unexpected unchecked cast warning produced by casting to a reified (unbounded wildcard) type

I had thought that casting to an unbounded wildcard type would never generate an "unchecked cast" warning because unbounded wildcard types are reified, and therefore the type information needed to safely complete the cast would be present at runtime, i.e. "cast to a generic class instance of any (unknown) type."

This method produces an unchecked cast warning at the indicated line:

    private static DateSerial.Metadata<?> metadataForName ( String className ) {

        Class<? extends DateSerial> cls;
        try {
            cls = Class.forName(className).asSubclass(DateSerial.class);
        } catch (ClassNotFoundException ex) {
            return null;
        }

-->     DateSerial.Metadata<?> result = (DateSerial.Metadata<?>) Kernel
            .obtainMetadata(cls)
            .orElse(null);
        return result;
    }

The method obtainMetadata() returns an Optional<T> where <T> is a bounded type paramater <T extends DateSerial<T>>. As you can deduce from the above code, the type argument supplied for <T> is ? extends DateSerial. A snippet of the obtainMetadata method appears below:

static <D extends DateSerial<D>> Optional<DateSerial.Metadata<D>> 
            obtainMetadata ( Class<D> cls ) {

    Optional<Method> exe = Arrays
            .stream(cls.getDeclaredMethods())
                :
                :
            .findAny();

    DateSerial.Metadata<?> k;  // returned object must have type Metadata.class
    try {
        k = (DateSerial.Metadata<?>) exe    // checked cast
                .get().invoke(null);
    } 
    catch (... ex) { return Optional.empty(); }

    if (k == null || (k.getImplClass() != cls)) return Optional.empty();

    @SuppressWarnings("unchecked")
    DateSerial.Metadata<D> result = (DateSerial.Metadata<D>) k;
    return Optional.of(result);
}

This method reflectively looks up a metadata object accessor method and invokes it. The invoked method must have a return type of Metadata.class. Note that the local variable k is assigned with a cast to an unbounded wildcard type. This line generates no warning.

By the end of this method, I have proved that the metadata object has the same type as the supplied Class<D> argument and so I may safely do the cast to the return type (the warning is suppressed because I've proved that it is safe). If <D> is ? extends DateSerial, I would expect the return type to be Optional<Metadata<? extends DateSerial>> but it isn't. For reasons that are not clear to me, the return type with the above type argument is a raw type Optional.

Two questions:

  1. Why does the cast to an unbounded wildcard type generate a warning in metadataForName() but not in obtainMetadata()?
  2. When the type argument to obtainMetadata() is ? extends DateSerial, why does that method return a raw type Optional?

Upvotes: 0

Views: 163

Answers (0)

Related Questions