Подтвердить что ты не робот

Java: newInstance класса, который не имеет конструктора по умолчанию

Я пытаюсь создать автоматическую систему тестирования (основанную на jUnit, но это не важно) для домашних заданий моих учеников. Им придется создавать конструкторы для некоторых классов, а также добавлять к ним некоторые методы. Позже, с функциями тестирования, которые я предоставляю, они будут проверять, все ли в порядке.

Что я хочу сделать, путем отражения, создайте новый экземпляр некоторого класса, который я хочу проверить. Проблема в том, что иногда нет конструктора по умолчанию. Меня это не волнует, Я хочу создать экземпляр и самостоятельно инициализировать переменные экземпляра. Есть ли способ сделать это? Простите, если это было задано раньше, но я просто не мог найти ответа.

Спасибо заранее.

4b9b3361

Ответ 1

Вызвать Class.getConstructor(), а затем Constructor.newInstance() передать соответствующие аргументы. Пример кода:

import java.lang.reflect.*;

public class Test {

    public Test(int x) {
        System.out.println("Constuctor called! x = " + x);
    }

    // Don't just declare "throws Exception" in real code!
    public static void main(String[] args) throws Exception {
        Class<Test> clazz = Test.class;
        Constructor<Test> ctor = clazz.getConstructor(int.class);
        Test instance = ctor.newInstance(5);           
    }
}

Ответ 2

Вот общее решение, которое не требует javassist или другого байт-кода "манипулятор". Хотя он предполагает, что конструкторы не делают ничего, кроме просто присваивания аргументов соответствующим полям, поэтому он просто выбирает первый конструктор и создает экземпляр со значениями по умолчанию (т.е. 0 для int, null для Object и т.д.).

private <T> T instantiate(Class<T> cls, Map<String, ? extends Object> args) throws Exception
{
    // Create instance of the given class
    final Constructor<T> constr = (Constructor<T>) cls.getConstructors()[0];
    final List<Object> params = new ArrayList<Object>();
    for (Class<?> pType : constr.getParameterTypes())
    {
        params.add((pType.isPrimitive()) ? ClassUtils.primitiveToWrapper(pType).newInstance() : null);
    }
    final T instance = constr.newInstance(params.toArray());

    // Set separate fields
    for (Map.Entry<String, ? extends Object> arg : args.entrySet()) {
        Field f = cls.getDeclaredField(arg.getKey());
        f.setAccessible(true);
        f.set(instance, arg.getValue());
    }

    return instance;
}

P.S. Работает с Java 1.5+. Решение также предполагает отсутствие диспетчера SecurityManager, который мог бы предотвратить вызов f.setAccessible(true).

Ответ 3

Если вы не использовали насмешливые фреймворки (например, ezmock), я настоятельно рекомендую вам попробовать.

Возможно, я ошибаюсь, и это может не помочь вам вообще, но из того, что я мог собрать из вашего поста, кажется, что насмешка может быть именно тем, что вы ищете (хотя я понимаю, что это не имеет никакого отношения к что вы просили.

Изменить: в ответ на комментарий.

Нет. Современные издевательские рамки позволяют создавать "Fake" экземпляр любого класса из "ничего" и передавать его, как если бы это был экземпляр класса. Ему не нужен интерфейс, он может быть любым классом. Также методы могут быть написаны сценарием, чтобы возвращать последовательность значений из простой, всегда возвращать "7" в "Когда вызывается с аргументом arg = 7 return 5 первым вызовом, 6 - вторым и 7 третьим".

Обычно он используется вместе с тестовыми фреймами, чтобы дать ссылочный класс перейти к тестируемому классу.

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

Ответ 4

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

protected List<Object> FillObject(ResultSet rs, String className)
    {
        List<Object> dList = new ArrayList<Object>();

        try
        {
            ClassLoader classLoader = GenericModel.class.getClassLoader();

            while (rs.next())
            {
                Class reflectionClass = classLoader.loadClass("models." + className);

                Object objectClass = reflectionClass.newInstance();

                Method[] methods = reflectionClass.getMethods();

                for(Method method: methods)
                {
                    if (method.getName().indexOf("set") > -1)
                    {
                        Class[] parameterTypes = method.getParameterTypes();

                        for(Class pT: parameterTypes)
                        {
                            Method setMethod = reflectionClass.getMethod(method.getName(), pT);

                            switch(pT.getName())
                            {
                                case "int":
                                    int intValue = rs.getInt(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, intValue);
                                    break;

                                case "java.util.Date":
                                    Date dateValue = rs.getDate(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, dateValue);
                                    break;

                                case "boolean":
                                    boolean boolValue = rs.getBoolean(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, boolValue);
                                    break;

                                default:
                                    String stringValue = rs.getString(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, stringValue);
                                    break;
                            }
                        }
                    }
                }

                dList.add(objectClass);
            }
        }
        catch (Exception e)
        {
            this.setConnectionMessage("ERROR: reflection class loading: " + e.getMessage());
        }

        return dList;
    }

Ответ 6

Вы можете распространять следующий исходный код с вашим назначением. Скажите учащимся включить его в исходный код. Их код не будет компилироваться, если они не закодируют класс Assignment с надлежащей подписью. Компилятор проверяет вам сертификат.

Тогда ваша программа тестирования не нуждается в отражении. Просто создайте AssignmentFactory и вызовите метод make с соответствующими аргументами.

Если вы используете эту идею, ваша новая задача будет заключаться в том, что некоторые ученики изменяют AssignmentFactory в соответствии со своим классом Assignment (нарушая вашу тестовую программу).

package assignment ;

public class AssignmentFactory
{
     public AssignmentFactory ( )
     {
           super ( ) ;
     }

     public AssignmentFactory make ( .... parameters )
     {
           return new Assignment ( .... arguments ) ;
     }
}