Мне нужно сделать несколько вызовов рефлексивных методов в Java. Эти вызовы будут включать методы, аргументы которых являются примитивными типами (int, double и т. д.). Способ указать такие типы при рефлексивном поиске метода - int.class, double.class и т. д.
Проблема в том, что я принимаю ввод от внешнего источника, который будет определять типы динамически. Следовательно, мне также нужно создавать эти ссылки на классы динамически. Представьте себе файл с разделителями, содержащий список имен методов со списками типов параметров:
doSomething int double
doSomethingElse java.lang.String boolean
Если бы вход был чем-то вроде java.lang.String, я знаю, что мог бы использовать Class.forName("java.lang.String") для этого экземпляра класса обратно. Есть ли способ использовать тот или иной метод, чтобы вернуть классы примитивного типа?
Редактировать:
Спасибо всем респондентам. Кажется очевидным, что нет встроенного способа чисто делать то, что я хочу, поэтому я соглашусь на повторное использование класса ClassUtils из среды Spring. Кажется, он содержит замену Class.forName (), которая будет работать с моими требованиями.




Экземпляры Class для примитивных типов доступны, как вы сказали, используя, например, int.class, но также можно получить те же значения, используя что-то вроде Integer.TYPE. Каждый примитивный класс-оболочка содержит статическое поле TYPE, которое имеет соответствующий экземпляр примитивного класса.
Вы не можете получить примитивный класс через forName, но вы можете получить его из легко доступного класса. Если вам абсолютно необходимо использовать отражение, вы можете попробовать что-то вроде этого:
Class clazz = Class.forName("java.lang.Integer");
Class intClass = clazz.getField("TYPE").get(null);
intClass.equals(int.class); // => true
Вы можете просто проверить, получили ли вы какой-то произвольный специальный тип (скажем: "int"), а затем выполнить перезапись соответствующим образом. Не существует строкового значения, которое можно передать в forName, которое будет извлекать класс примитивного типа.
Вместо этого вы можете просто сделать Integer.TYPE
Инфраструктура Spring содержит служебный класс ClassUtils, который содержит статический метод forName. Этот метод можно использовать именно для той цели, которую вы описали.
Если вы не хотите зависеть от Spring: исходный код метода можно найти е. грамм. здесь в их общедоступном репозитории. Исходный код класса лицензирован по модели Apache 2.0.
Однако обратите внимание, что алгоритм использует жестко запрограммированную карту примитивных типов.
Редактировать: Спасибо комментаторам Давиду Хорвату и Патрику за указание на неработающую ссылку.
Источник файла ClassUtils.java по этой ссылке поврежден. Этот смотрит на меня
@Patrick: К сожалению, ваша ссылка тоже не работает. Однако фреймворк Spring теперь доступен на GitHub. github.com/spring-projects/spring-framework/blob/master/…
Возможно, вам просто нужно отобразить примитивы, а для остальных классов выполнить метод forName:
Я бы сделал что-то вроде:
void someWhere(){
String methodDescription = "doSomething int double java.lang.Integer java.lang.String"
String [] parts = methodDescription.split();
String methodName= parts[0]
Class [] paramsTypes = getParamTypes( parts ); // Well, not all the array, but a, sub array from 1 to arr.length..
Method m = someObject.class.getMethod( methodName, paramTypes );
etc. etc etc.
}
public Class[] paramTypes( String [] array ){
List<Class> list = new ArrayList<Class>();
for( String type : array ) {
if ( builtInMap.contains( type )) {
list.add( builtInMap.get( type ) );
}else{
list.add( Class.forName( type ) );
}
}
return list.toArray();
}
// That's right.
Map<String,Class> builtInMap = new HashMap<String,Class>();{
builtInMap.put("int", Integer.TYPE );
builtInMap.put("long", Long.TYPE );
builtInMap.put("double", Double.TYPE );
builtInMap.put("float", Float.TYPE );
builtInMap.put("bool", Boolean.TYPE );
builtInMap.put("char", Character.TYPE );
builtInMap.put("byte", Byte.TYPE );
builtInMap.put("void", Void.TYPE );
builtInMap.put("short", Short.TYPE );
}
То есть создайте карту, в которой хранятся типы примитивов, и если описание принадлежит примитиву, используйте сопоставленный класс. Эта карта также может быть загружена из внешнего файла конфигурации, чтобы добавить гибкости, поэтому вы добавляете String как встроенный вместо java.lang.String или, возможно, имеете такой метод.
"doSomething строка да | нет"
В проектах ОС, таких как Struts, Hibernate, Spring и Apache libs (и это лишь некоторые из них), много такого кода, поэтому вам не нужно начинать с нуля.
КСТАТИ. Я не компилировал приведенный выше код, но я почти уверен, что он работает с небольшими изменениями, не голосуйте за это.
То же самое и внутри ObjectInputStream: он просто выполняет поиск forName () и, если он генерирует исключение ClassNotFoundException, выполняет поиск по карте.
Спасибо за ответ. Я буду повторно использовать служебный метод в Spring ClassUtils, который использует этот процесс поиска.
Где вы используете этот "низкоуровневый" код? Внимательно изучите, как защитить свою службу, иначе вы можете получить вызов "System.exit ()" от вашего удаленного клиента и поверьте мне, сервер отключится!
К сожалению, некоторые методы класса не обрабатывают примитивы согласованным образом. Обычный способ обойти это в forName - создать такую таблицу;
private static final Map<String, Class> BUILT_IN_MAP =
new ConcurrentHashMap<String, Class>();
static {
for (Class c : new Class[]{void.class, boolean.class, byte.class, char.class,
short.class, int.class, float.class, double.class, long.class})
BUILT_IN_MAP.put(c.getName(), c);
}
public static Class forName(String name) throws ClassNotFoundException {
Class c = BUILT_IN_MAP.get(name);
if (c == null)
// assumes you have only one class loader!
BUILT_IN_MAP.put(name, c = Class.forName(name));
return c;
}
В следующем коде рассказывается о том, как получить класс примитивного типа, имя поля которого известно, например в данном случае - sampleInt.
public class CheckPrimitve {
public static void main(String[] args) {
Sample s = new Sample();
try {
System.out.println(s.getClass().getField("sampleInt").getType() == int.class); // returns true
System.out.println(s.getClass().getField("sampleInt").getType().isPrimitive()); // returns true
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
}
class Sample {
public int sampleInt;
public Sample() {
sampleInt = 10;
}
}
Также можно проверить, является ли данное значение примитивным или нет, получив соответствующий класс-оболочку или значение поля.
public class CheckPrimitve {
public static void main(String[] args) {
int i = 3;
Object o = i;
System.out.println(o.getClass().getSimpleName().equals("Integer")); // returns true
Field[] fields = o.getClass().getFields();
for(Field field:fields) {
System.out.println(field.getType()); // returns {int, int, class java.lang.Class, int}
}
}
}
Спасибо за ответ! Хотя фрагмент кода может ответить на этот вопрос, все же неплохо добавить дополнительную информацию, например, объяснить и т. д.
Apache Commons Lang имеет ClassUtils.getClass (строка), который поддерживает примитивные типы.
У меня не работает. У меня Field типа long. Вызов ClassUtils.getClass(field.getType().toString()) просто возвращает long, а не java.lang.Long.
Google Guava предлагает com.google.common.primitives.Primitives для такого рода вещей.
Вы можете использовать этот код :)
/**
* Get an array class of the given class.
*
* @param klass to get an array class of
* @param <C> the targeted class
* @return an array class of the given class
*/
public static <C> Class<C[]> arrayClass(Class<C> klass) {
return (Class<C[]>) Array.newInstance(klass, 0).getClass();
}
/**
* Get the class that extends {@link Object} that represent the given class.
*
* @param klass to get the object class of
* @return the class that extends Object class and represent the given class
*/
public static Class<?> objectiveClass(Class<?> klass) {
Class<?> component = klass.getComponentType();
if (component != null) {
if (component.isPrimitive() || component.isArray())
return Reflect.arrayClass(Reflect.objectiveClass(component));
} else if (klass.isPrimitive()) {
if (klass == char.class)
return Character.class;
if (klass == int.class)
return Integer.class;
if (klass == boolean.class)
return Boolean.class;
if (klass == byte.class)
return Byte.class;
if (klass == double.class)
return Double.class;
if (klass == float.class)
return Float.class;
if (klass == long.class)
return Long.class;
if (klass == short.class)
return Short.class;
}
return klass;
}
/**
* Get the class that don't extends {@link Object} from the given class.
*
* @param klass to get the non-object class of
* @return the non-object class of the given class
* @throws IllegalArgumentException when the given class don't have a primitive type
*/
public static Class<?> primitiveClass(Class<?> klass) {
if (klass == Character.class)
return char.class;
if (klass == Integer.class)
return int.class;
if (klass == Boolean.class)
return boolean.class;
if (klass == Byte.class)
return byte.class;
if (klass == Double.class)
return double.class;
if (klass == Float.class)
return float.class;
if (klass == Long.class)
return long.class;
if (klass == Short.class)
return short.class;
throw new IllegalArgumentException(klass + " don't have a primitive type");
}
Это правда, и я не знал об этом. Однако в моей ситуации мне нужен способ указать, что любой метод может быть вызван рефлексивно. Следовательно, если я приму java.lang.Integer для представления значений типа int, я потеряю возможность вызывать методы, принимающие значения Integer.