Документация для Class<T>.getConstructors()
раскрывает проблему:
Constructor<?>[] getConstructors()
: обратите внимание, что хотя этот метод возвращает массив объектов Constructor<T>
(это массив конструкторов из этого класса), тип возвращаемого значения этого метода Constructor<?>[]
, а не Constructor<T>[]
как и следовало ожидать. Этот менее информативный тип возвращаемого значения необходим, поскольку после возврата из этого метода массив можно изменить, чтобы он содержал Constructor
объекты для различных классов, что нарушало бы гарантии типа Constructor<T>[]
.
Сравните это с Class<T>.getConstructor()
перегрузкой:
Constructor<T> getConstructor(Class<?>... parameterTypes)
Эта перегрузка фактически возвращает Constructor<T>
.
В вашем случае, поскольку у вас Class<? extends Algorithm> alg
, alg.getConstructor(parameterTypes)
возвращает Constructor<? extends Algorithm>
(что, кстати, НЕ может быть безопасно приведено к Constructor<Algorithm>
).
Когда это возможно, то есть когда вы знаете типы параметров, вы всегда должны предпочитать от getConstructor(parameterTypes)
до getConstructors()[arbitraryIndex]
, так как с последними вы никогда не будете полностью уверены, какой конструктор будет выбран. Выбор, основанный на типах параметров, наиболее специфичен и, как показано здесь, также более удобен для компилятора в отношении информации об общем типе.
Если под getConstructors()[0]
вы действительно хотели получить нулевой конструктор, просто сделайте это вместо:
Constructor<? extends Algorithm> nullaryConstructor = alg.getConstructor();
Резюме
Так что ваша проблема была в два раза:
getConstructors()
возвращает Constructor<?>[]
, что означает потерю всей информации о типах
- Даже если информация о типе не потеряна (например, при использовании перегрузки
getConstructor(parameterTypes)
), вы все равно получите в лучшем случае Constructor<? extends Algorithm>
(не Constructor<Algorithm>
).
Class<T>.getConstructor(parameterTypes)
возвращает Constructor<T>
- В вашем случае параметр типа
T
равен ? extends Algorithm
Смотри также
Смежные вопросы
Обсуждение
К сожалению, getConstructors()
возвращает массив, потому что, как объяснено в документации, потому что, так как массивы ковариантны, это заставило подпись к Constructor<?>
, потеряв информацию общего типа. Тот факт, что массивы являются изменяемыми, также означает, что getConstructors()
должен каждый раз создавать новый экземпляр массива!
System.out.println(int.class.getConstructors().length); // prints "0"
System.out.println(
int.class.getConstructors() == int.class.getConstructors()
); // prints "false"!
В идеале getConstructors()
должно возвращать List<Constructor<T>>
, которое должно быть заключено в Collections.unmodifiableList
; это не только сохранит информацию о типе, но Class<T>
может также возвращать один и тот же объект List
на каждый getConstructors()
.
Подробнее об этом читайте в статье Effective Java 2nd Edition, пункт 25. Предпочитайте списки массивам .