Passing generic subtype class information to superclass in Java

25,736

Solution 1

The "pattern" (idiom) of passing an instance of Class<T> (typically to the constructor) is using Class Literals as Runtime-Type Tokens, and is used to keep a runtime reference to the generic type, which is otherwise erased.

The solution is firstly to change the token class bound to:

Class<? extends T>

and then to put a similar requirement on your generic subclass as you did with your super class; have the concrete class pass a type token, but you can type it properly as a parameter:

These classes compile without casts or warnings:

public abstract class Abstract<T extends Abstract<T>> {
    private final Class<? extends T> subClass;

    protected Abstract(Class<? extends T> subClass) {
        this.subClass = subClass;
    }
}

public class NonGeneric extends Abstract<NonGeneric> {
    public NonGeneric() {
        super(NonGeneric.class);
    }
}

public class Generic<T> extends Abstract<Generic<T>> {
    public Generic(Class<? extends Generic<T>> clazz) {
        super(clazz);
    }
}

And finally at the concrete class, if you declare the usage as its own class, it doesn't require a cast anywhere:

public class IntegerGeneric extends Generic<Integer> {
    public IntegerGeneric() {
        super(IntegerGeneric.class);
    }
}

I haven't figured out how to create an instance of Generic (anonymous or not) without a cast:

// can someone fill in the parameters without a cast?
new Generic<Integer>(???);     // typed direct instance
new Generic<Integer>(???) { }; // anonymous

I don't think it's possible, but I welcome being shown otherwise.

Solution 2

The major problem you have got here is, there is no class literal for concrete parameterized type. And that makes sense, since parameterized types don't have any runtime type information. So, you can only have class literal with raw types, in this case Generic.class.

Reference:

Well, that's fine, but Generic.class gives you a Class<Generic> which is not compatible with Class<Generic<T>>. A workaround is to find a way to convert it to Class<Generic<T>>, but that too you can't do directly. You would have to add an intermediate cast to Class<?>, which represents the family of all the instantiation of Class. And then downcast to Class<Generic<T>>, which will remove the compiler error, though you will an unchecked cast warning. You can annotate the constructor with @SuppressWarnings("unchecked") to remove the warning.

class Generic<T> extends Abstract<Generic<T>> {     
    public Generic() {
        super((Class<Generic<T>>)(Class<?>)Generic.class);
    }
}
Share:
25,736
errantlinguist
Author by

errantlinguist

Updated on February 20, 2020

Comments

  • errantlinguist
    errantlinguist about 4 years

    I've long used an idiom in Java for using the class information of a (non-abstract) class in the methods of its (generally abstract) ancestor class(es) (unfortunately I can't find the name of this pattern):

    public abstract class Abstract<T extends Abstract<T>> {
        private final Class<T> subClass;
    
        protected Abstract(Class<T> subClass) {
            this.subClass = subClass;
        }
    
        protected T getSomethingElseWithSameType() {
            ....
        }
    }
    

    An example of a subclass thereof:

    public class NonGeneric extends Abstract<NonGeneric> {
        public NonGeneric() {
            super(NonGeneric.class);
        }
    }
    

    However, I'm having trouble defining a subclass of Abstract which has its own generic parameters:

    public class Generic<T> extends Abstract<Generic<T>> {
        public Generic() {
            super(Generic.class);
        }
    }
    

    This example is not compilable; likewise, it is not possible to specify the generic types using e.g. Generic<T>.class or even to use a wildcard like Generic<?>.

    I also tried replacing the declaration of generic type T in the superclass to ? extends T, but that isn't compilable either.

    Is there any way I can get this pattern to work with generic base classes?