Конструктор вызовов Java Reflection с примитивными типами
У меня есть метод в моей тестовой среде, который создает экземпляр класса, в зависимости от параметров, переданных в:
public void test(Object... constructorArgs) throws Exception {
Constructor<T> con;
if (constructorArgs.length > 0) {
Class<?>[] parameterTypes = new Class<?>[constructorArgs.length];
for (int i = 0; i < constructorArgs.length; i++) {
parameterTypes[i] = constructorArgs[i].getClass();
}
con = clazz.getConstructor(parameterTypes);
} else {
con = clazz.getConstructor();
}
}
Проблема заключается в том, что это не работает, если конструктор имеет примитивные типы:
public Range(String name, int lowerBound, int upperBound) { ... }
.test("a", 1, 3);
Результаты в:
java.lang.NoSuchMethodException: Range.<init>(java.lang.String, java.lang.Integer, java.lang.Integer)
Примитивные ints автоматически помещаются в объектные версии, но как мне их вернуть для вызова конструктора?
Ответы
Ответ 1
Используйте Integer.TYPE
вместо Integer.class
.
В соответствии с Javadocs это "экземпляр класса, представляющий примитивный тип int
".
Вы также можете использовать int.class
. Это ярлык для Integer.TYPE
. Не только классы, даже для примитивных типов, вы можете сказать type.class
в Java.
Ответ 2
Для ссылки на примитивные типы используйте, например:
Integer.TYPE;
Вам нужно будет знать, какие аргументы, переданные в ваш метод, являются примитивными значениями. Вы можете сделать это с помощью:
object.getClass().isPrimitive()
Ответ 3
Так как примитивные типы автобоксированы, вызов getConstructor(java.lang.Class<?>... parameterTypes)
завершится с ошибкой. Вам нужно будет вручную прокрутить доступные конструкторы. Если все типы совпадают, вы в порядке. Если некоторые типы не совпадают, но требуемый тип является примитивным И доступным типом является соответствующий класс-оболочка, тогда вы можете использовать этот конструктор. См. Ниже:
static <C> Constructor<C> getAppropriateConstructor(Class<C> c, Object[] initArgs){
if(initArgs == null)
initArgs = new Object[0];
for(Constructor con : c.getDeclaredConstructors()){
Class[] types = con.getParameterTypes();
if(types.length!=initArgs.length)
continue;
boolean match = true;
for(int i = 0; i < types.length; i++){
Class need = types[i], got = initArgs[i].getClass();
if(!need.isAssignableFrom(got)){
if(need.isPrimitive()){
match = (int.class.equals(need) && Integer.class.equals(got))
|| (long.class.equals(need) && Long.class.equals(got))
|| (char.class.equals(need) && Character.class.equals(got))
|| (short.class.equals(need) && Short.class.equals(got))
|| (boolean.class.equals(need) && Boolean.class.equals(got))
|| (byte.class.equals(need) && Byte.class.equals(got));
}else{
match = false;
}
}
if(!match)
break;
}
if(match)
return con;
}
throw new IllegalArgumentException("Cannot find an appropriate constructor for class " + c + " and arguments " + Arrays.toString(initArgs));
}
Ответ 4
вы можете написать
int[].class.getComponentType()
или
Integer.TYPE
или
int.class
Ответ 5
Если примитивное значение int
автобоксируется в объект Integer
, оно больше не является примитивным. Вы не можете указать из экземпляра Integer
, был ли он int
в какой-то момент.
Я бы предложил передать два массива в метод test
: один с типами и другой со значениями. Он также устранит двусмысленность, если у вас есть конструктор MyClass(Object)
и значение строки передачи (getConstructor
будет искать конструктор String
).
Кроме того, вы не можете определить ожидаемый тип параметра, если значение параметра равно null.