Как вызвать правильный фабричный метод универсального типа?
Я пытаюсь написать универсальный класс, который может создать экземпляр своего универсального типа, вызывая статический метод фабрики класса этого типа:
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());
}