How to call the correct factory method of a generic type?

I'm trying to write a generic class, that can create an instance of its generic type, by invoking a static factory method of that type's class:

class Test<T extends Parent> {

    public static void main(String[] args) {
        new Test<Child>();
    }

    private Test() {
        System.out.println(Child.newInstance());
        System.out.println(T.newInstance());
    }
}

abstract class Parent {
    static <T extends Parent> T newInstance() {
        return null;
    }
}

class Child extends Parent {
    static Child newInstance() {
        return new Child();
    }
}

I expected that Child.newInstance() and T.newInstance() would call the same method, since the type T was set as Child. But instead T.newInstance() calls its parent's class method and returns null, while the direct call Child.newInstance() returns a new Child object. Can someone explain me please, where my misunderstanding in the Java Generics logic is, and if there is any other clean way to create an instance of a generic type?

Edit: I'm not trying to override a static method, I'm just trying to hide it.

2 answers

  • answered 2017-11-12 19:53 Joe C

    Thanks to type erasure, we do not know what the type argument is. Thus, at run time, we do not know whether it is a Test<Parent>, a Test<Child>, a Test<Chicken>, etc.

    If you want to use the type parameter, you will need to pass in the relevant Class object and use reflection on it, like so:

    public class Test<T extends Parent> {
    
        public static void main(String[] args) {
            new Test<Child>(Child.class);
        }
    
        private Test(Class<T> clazz) {
            System.out.println(Child.newInstance());
            try {
                System.out.println(clazz.getDeclaredMethod("newInstance").invoke(null));
            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
    }
    

  • answered 2017-11-12 20:01 luckydog32

    Due to java's type erasure, during run time every generic object is replaced with its upper-bound. So in your code:

    class Test<T extends Parent> {
    
    private Test() {
        System.out.println(Child.newInstance());
        System.out.println(T.newInstance());
    }
    

    }

    Since Parent is the upper bound, test() ends up compiling into the following, regardless of what generic parameter you specify outside of the class:

    private Test() {
        System.out.println(Child.newInstance());
        System.out.println(Parent.newInstance());
    }