Java: newInstance класса, у которого нет конструктора по умолчанию
Я пытаюсь создать среду автоматического тестирования (на основе jUnit, но это не важно) для домашней работы моих студентов. Им придется создавать конструкторы для некоторых классов, а также добавлять к ним некоторые методы. Позже, с предоставленными мною функциями тестирования, они проверит, все ли в порядке.
Что я хочу сделать, это поразмышлять, создать новый экземпляр класса, который я хочу протестировать. Проблема в том, что иногда нет конструктора по умолчанию. Меня это не волнует, я хочу создать экземпляр и инициализировать переменные экземпляра самостоятельно. Есть ли способ сделать это? Извините, если об этом уже спрашивали, но я просто не смог найти ответа.
Заранее спасибо.
6 ответов
Вызов Class.getConstructor()
а потом Constructor.newInstance()
passing in the appropriate arguments. Образец кода:
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);
}
}
Вот общее решение, которое не требует javassist или другого "манипулятора" байт-кода. Хотя предполагается, что конструкторы не делают ничего, кроме простого присвоения аргументов соответствующим полям, поэтому он просто выбирает первый конструктор и создает экземпляр со значениями по умолчанию (т. Е. 0 для int, ноль для 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;
}
PS Работает с Java 1.5+. Решение также предполагает отсутствие менеджера SecurityManager, который мог бы предотвратить вызов f.setAccessible(true)
,
Если вы еще не использовали фальшивые фреймворки (например, ezmock), я настоятельно рекомендую вам попробовать.
Я могу ошибаться, и это может вам совсем не помочь, но из того, что я могу почерпнуть из вашего поста, кажется вероятным, что насмешка может быть именно тем, что вы ищете (хотя я признаю, что это не имеет ничего общего с тем, что вы просили) за.
Изменить: в ответ на комментарий.
Нет, современные фальшивые фреймворки позволяют вам создавать "поддельный" экземпляр любого класса из "ничего" и передавать его так, как если бы он был экземпляром класса. Ему не нужен интерфейс, это может быть любой класс. Также могут быть написаны методы для возврата последовательности значений из простого всегда возврата "7" в "При вызове с аргументом arg=7 возвращаем 5 первого вызова, 6 второго и 7 третьего".
Обычно он используется в сочетании со средами тестирования, чтобы дать ссылочный класс для перехода в класс, который вы тестируете.
Это может быть не совсем то, что вы ищете, но вы упомянули модульное тестирование и ручную инициализацию переменных, поэтому казалось, что это то, что в конечном итоге может пригодиться.
Я использовал следующий код для создания списка универсальных объектов любого типа передаваемого имени класса. Он использует все методы набора внутри класса, чтобы установить все значения, передаваемые через набор результатов. Я публикую это на тот случай, если кому-то будет интересно.
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;
}
Вы можете распространять следующий исходный код с вашим заданием. Скажите студентам включить его в свой исходный код. Их код не скомпилируется, если они не кодируют класс Assignment с правильной подписью. Компилятор выполняет проверку подписи за вас.
Тогда вашей программе тестирования не нужно использовать рефлексию. Просто создайте экземпляр класса AssignmentFactory и вызовите метод make с правильными аргументами.
Если вы воспользуетесь этой идеей, ваши новые задачи будут заключаться в том, чтобы некоторые ученики изменили AssignmentFactory в соответствии со своим классом Assignment (нарушив вашу программу тестирования).
package assignment ;
public class AssignmentFactory
{
public AssignmentFactory ( )
{
super ( ) ;
}
public AssignmentFactory make ( .... parameters )
{
return new Assignment ( .... arguments ) ;
}
}
Вы можете использовать Class.getConstructor или Class.getConstructors, а затем использовать метод Constructor.newInstance для инициализации объекта, который вы хотите использовать.