Как вызвать правильный фабричный метод универсального типа?

Я пытаюсь написать универсальный класс, который может создать экземпляр своего универсального типа, вызывая статический метод фабрики класса этого типа:

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();
    }
}

Я ожидал что Child.newInstance() а также T.newInstance() вызовет тот же метод, так как тип T был установлен как Child, Но вместо этого T.newInstance() вызывает метод класса своего родителя и возвращает nullв то время как прямой звонок Child.newInstance() возвращает новый Child объект. Может кто-нибудь объяснить мне, пожалуйста, где мое недоразумение в логике Java Generics, и если есть какой-либо другой чистый способ создания экземпляра универсального типа?

Изменить: я не пытаюсь переопределить статический метод, я просто пытаюсь скрыть его.

2 ответа

Решение

Благодаря стиранию типов мы не знаем, что такое аргумент типа. Таким образом, во время выполнения мы не знаем, является ли это Test<Parent>, Test<Child>, Test<Chicken>, так далее.

Если вы хотите использовать параметр типа, вам нужно будет передать соответствующий Class объект и использовать отражение на нем, например, так:

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);
        }
    }
}

Из-за стирания типа java во время выполнения каждый общий объект заменяется его верхней границей. Итак, в вашем коде:

class Test<T extends Parent> {

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

}

поскольку Parent верхняя граница, test() в итоге компилируется в следующее, независимо от того, какой универсальный параметр вы указываете вне класса:

private Test() {
    System.out.println(Child.newInstance());
    System.out.println(Parent.newInstance());
}
Другие вопросы по тегам