Ответ 1
Динамическое создание объектов может быть довольно сложным, и ваш сценарий затрагивает несколько аспектов:
- преобразование значений объекта из
String
в соответствующий тип - загрузка правого класса из имени класса и создание экземпляра
- присвоение этих значений объекту
Тщательное обсуждение каждого из этих пунктов займет целую главу в беззастенчивом обращении с Java как динамическим языком. Но, предполагая, что у вас нет времени изучать эти тонкости или зависеть от какой-то огромной библиотеки сторонних разработчиков, дайте возможность взломать что-то, что вас встретит. Пожалуйста, держите руки внутри автомобиля в любое время, так как поездка будет ухабистой.
Сначала разрешите проблему преобразования типов. Значения представлены как Strings
, но ваш объект будет хранить их как double
, long
, int
и т.д. Поэтому нам нужна функция, которая анализирует String
в соответствующем типе цели:
static Object convert(Class<?> target, String s) {
if (target == Object.class || target == String.class || s == null) {
return s;
}
if (target == Character.class || target == char.class) {
return s.charAt(0);
}
if (target == Byte.class || target == byte.class) {
return Byte.parseByte(s);
}
if (target == Short.class || target == short.class) {
return Short.parseShort(s);
}
if (target == Integer.class || target == int.class) {
return Integer.parseInt(s);
}
if (target == Long.class || target == long.class) {
return Long.parseLong(s);
}
if (target == Float.class || target == float.class) {
return Float.parseFloat(s);
}
if (target == Double.class || target == double.class) {
return Double.parseDouble(s);
}
if (target == Boolean.class || target == boolean.class) {
return Boolean.parseBoolean(s);
}
throw new IllegalArgumentException("Don't know how to convert to " + target);
}
Тьфу. Это уродливо и обрабатывает только внутренние типы. Но мы не ищем совершенства здесь, верно? Поэтому, пожалуйста, при необходимости улучшите. Обратите внимание, что преобразование из String
в какой-либо другой тип фактически является формой десериализации, поэтому вы устанавливаете ограничения на своих клиентов (кто бы ни дал вам Strings
), чтобы предоставлять свои значения в определенных форматах. В этом случае форматы определяются поведением методов parse
. Упражнение 1: В какой-то момент в будущем измените формат в обратном несовместимом виде, чтобы навлечь кого-то гнев.
Теперь сделаем фактическое создание:
static Object instantiate(List<String> args, String className) throws Exception {
// Load the class.
Class<?> clazz = Class.forName(className);
// Search for an "appropriate" constructor.
for (Constructor<?> ctor : clazz.getConstructors()) {
Class<?>[] paramTypes = ctor.getParameterTypes();
// If the arity matches, let use it.
if (args.size() == paramTypes.length) {
// Convert the String arguments into the parameters' types.
Object[] convertedArgs = new Object[args.size()];
for (int i = 0; i < convertedArgs.length; i++) {
convertedArgs[i] = convert(paramTypes[i], args.get(i));
}
// Instantiate the object with the converted arguments.
return ctor.newInstance(convertedArgs);
}
}
throw new IllegalArgumentException("Don't know how to instantiate " + className);
}
Мы принимаем здесь много ярлыков, но эй, это не сикстинская часовня, которую мы создаем. Просто загрузите класс и найдите конструктор, число параметров которого соответствует количеству аргументов (т.е. Arity). Перегруженные конструкторы одной и той же реальности? Нет, не собираюсь работать. С переменным числом аргументов? Нет, не собираюсь работать. Непубличные конструкторы? Нет, не собираюсь работать. И если вы не можете гарантировать, что ваш класс предоставит конструктор, который задает все поля, подобные вашему примеру TempStruct
, то я назову его днем и возьму пиво, потому что этот подход DOA.
Как только мы найдем конструктор, перейдем к args String
, чтобы преобразовать их в типы, ожидаемые конструктором. Предполагая, что это работает, мы затем вызываем конструктор через отражение, махаем волшебной палочкой и говорим об абракадабре. Voilà: у вас есть новый объект.
Попробуйте это с помощью чрезвычайно надуманного примера:
public static void main(String[] args) throws Exception {
TempStruct ts =
(TempStruct)instantiate(
Arrays.asList("373.15", "Kelvin"),
TempStruct.class.getName());
System.out.println(
ts.getClass().getSimpleName() + " " +
ts.tempValue + " " +
ts.unitOfMeasurement);
}
Вывод:
TempStruct 373.15 Kelvin
СЛАВНОЕ