0

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?
scottb
  • 9,908
  • 3
  • 40
  • 56
  • Since `DateSerial` is apparently generic, leaving it without a type parameter means you are using raw types. Does it work if you bound `DateSerial`: `DateSerial>.Metadata>`? – Andy Turner Jan 23 '19 at 16:04
  • I'll try it. `Metadata` is a static nested class in `DateSerial` so I don't expect that a type parameter on `DateSerial` should have any effect. The name `DateSerial` here really ought to serve only as a qualifier, as I understand it. – scottb Jan 23 '19 at 16:39
  • Yeah, this is down to raw types. Unfortunately, you're really stuck with raw types if you are using classes, because a Class always has an erased type parameter. – Andy Turner Jan 23 '19 at 16:44

0 Answers0