Динамически создавать объект в java из имени класса и устанавливать поля класса с помощью списка с данными

У меня есть список, содержащий данные со строковым типом → ["classField1", "classField2", "classField3"]

У меня есть метод (myMethod(List list, String className)), который принимает в качестве параметра List. Таким образом, я могу передать этот список через параметр myMethod (список List, String className).

В myMethod, я хочу создать один объект, который будет экземпляром классаName, который является вторым параметром. После этого я хочу установить поля класса, используя данные списка. Из-за того, что я хочу динамически получать поля класса, результат выше - это то, что я должен отображать каждое значение String списка, тип каждого поля этого класса.

Я уверен, что порядок строк внутри Списка находится в правильном порядке и соответствует полям класса с тем же порядком.

Кто-нибудь знает, как это сделать?

Пример:

["StringtempValue", "StringUnitOfMeasurement"] = >

Создать объект экземпляра:

public class TempStruct {

   private double tempValue;
   private String unitOfMeasurement;

   public TempStruct(double tempValue, String unitOfMeasurement) {
     this.tempValue = tempValue;
     this.unitOfMeasurement = unitOfMeasurement;
   }

}

Я пытаюсь дать решение следующим образом:

На самом деле я хочу создать объект существующего класса, и я попытался сделать это с отражением. Я использую следующий код:

Class<?> cls = Class.forName(name);
Object clsInstance = (Object) cls.newInstance();
Field[] objectFields = clsInstance.getClass().getDeclaredFields();

Но я получаю исключение во 2-й строке, когда он пытается создать новый объект. Поскольку @JB Nijet сказал, что я не знал, что метод getDeclaredFields() не возвращает отсортированные поля.

На самом деле, у меня есть метод, который принимает только List of Strings, поэтому, используя отражение, я конвертирую объект в List of string, а после этого хочу сделать обратное. Я не думал о другом способе этого.

Ответ 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

СЛАВНОЕ