javagenericsrecursionnested-generics

Generics in recursive method


I've been banging my head on the table over this for a few hours now.

I have this Map:
private static Map<Class<? extends BaseClass>, Predicate<? extends BaseClass>> constraints;

And I have this method:

public static <C extends BaseClass> Predicate<C> getConstraints(Class<? super C> clazz) {
    if (clazz == BaseClass.class) {
        return (Predicate<C>) constraints.getOrDefault(BaseClass.class, x -> true);
    }
    Class<? super C> superClass = clazz.getSuperclass();
    return constraints.getOrDefault(clazz, x -> true).and(getConstraints(superClass));
}

All this is supposed to do is to chain all constraints for a certain class (and the superclasses) in a Predicate<T> using the and() method. The generics seem logical to me but I still get an error when making the recursive call. getConstraints(Class<? super C>) cannot be applied to Class<capture of ? super E>. I don't get it because superClass is of the type Class<? super C> which is exactly the parameter that my method accepts. Any ideas?


Solution

  • You could use a type variable that represents the superclass:

    @SuppressWarnings("unchecked")
    public static <S extends BaseClass, C extends S> Predicate<C> getConstraints(Class<C> clazz) {
        if (clazz == BaseClass.class) {
            return (Predicate<C>) constraints.getOrDefault(BaseClass.class, x -> true);
        }
        Class<S> superClass = (Class<S>) clazz.getSuperclass();
        Predicate<C> p1 = (Predicate<C>) constraints.getOrDefault(clazz, x -> true);
        Predicate<S> p2 = getConstraints(superClass);
        return p1.and(p2);
    }
    

    You will also have to cast the result of getOrDefault, since putting the Predicate in the map 'forgets' the type information, and you get back a generic Predicate<? extends BaseClass>, which you can't use directly.