Java: nueva instancia de la clase que no tiene un constructor predeterminado

Estoy intentando construir un marco de prueba automático (basado en jUnit, pero eso no es importante) para la tarea de mis alumnos. Tendrán que crear constructores para algunas clases y también agregarles algunos métodos. Más tarde, con las funciones de prueba que proporciono, verificarán si funcionó bien.

Lo que quiero hacer es, por reflexión , crear una nueva instancia de alguna clase que quiera probar. El problema es que, a veces, no hay un constructor predeterminado . No me importa eso, quiero crear una instancia e inicializar las variables de instancia yo mismo . ¿Hay alguna forma de hacer esto? Lo siento si esto se ha preguntado antes, pero simplemente no he podido encontrar ninguna respuesta.

Gracias por adelantado.

Llame Class.getConstructor() y luego Constructor.newInstance() pasando los argumentos apropiados. Código de muestra:

 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 clazz = Test.class; Constructor ctor = clazz.getConstructor(int.class); Test instance = ctor.newInstance(5); } } 

Aquí hay una solución genérica que no requiere javassist u otro bytecode “manipulador”. Aunque asume que los constructores no hacen nada más que simplemente asignar argumentos a los campos correspondientes, por lo que simplemente selecciona el primer constructor y crea una instancia con valores predeterminados (es decir, 0 para int, null para Object, etc.).

 private  T instantiate(Class cls, Map args) throws Exception { // Create instance of the given class final Constructor constr = (Constructor) cls.getConstructors()[0]; final List params = new ArrayList(); 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 arg : args.entrySet()) { Field f = cls.getDeclaredField(arg.getKey()); f.setAccessible(true); f.set(instance, arg.getValue()); } return instance; } 

PS funciona con Java 1.5+. La solución también supone que no hay un administrador de SecurityManager que pueda evitar la invocación de f.setAccessible(true) .

Si no ha utilizado marcos de burla (como ezmock), le recomiendo que pruebe.

Puedo estar equivocado y eso puede que no te ayude en absoluto, pero por lo que pude deducir de tu publicación, parece posible que la burla sea exactamente lo que estás buscando (aunque reconozco que no tiene nada que ver con lo que preguntaste para.

Editar: en respuesta al comentario.

No, los marcos de burla modernos te permiten crear una instancia “Falsa” de cualquier clase desde “nada” y pasarla como si fuera una instancia de la clase. No necesita una interfaz, puede ser cualquier clase. También los métodos se pueden progtwigr para devolver una secuencia de valores de un retorno siempre simple “7” a “Cuando se llama con un arg = 7, devuelve 5 la primera llamada, 6 el segundo y 7 el tercero”.

Por lo general, se utiliza junto con marcos de prueba para dar una clase de referencia para pasar a la clase que está probando.

Puede que esto no sea exactamente lo que estás buscando, pero mencionaste las pruebas unitarias y las variables de inicialización manual, por lo que parecía que esto es algo que eventualmente podría ser útil.

Utilicé el siguiente código para crear una lista de objetos generics de cualquier tipo de nombre de clase que se haya pasado. Utiliza todos los métodos establecidos dentro de la clase para establecer todos los valores pasados ​​a través del conjunto de resultados. Estoy publicando esto en caso de que a alguien también le interese.

 protected List FillObject(ResultSet rs, String className) { List dList = new ArrayList(); 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; } 

Puede usar Class.getConstructor o Class.getConstructors y luego usar el método Constructor.newInstance para inicializar el objeto que desea usar.

Puede distribuir el siguiente código fuente con su tarea. Indique a los alumnos que lo incluyan en su código fuente. Su código no se comstackrá a menos que codifique una clase de Asignación con la firma adecuada. El comstackdor hace la verificación de la firma por usted.

Entonces su progtwig de prueba no necesita usar reflexión. Simplemente crea una instancia de AssignmentFactory y llama al método make con los argumentos adecuados.

Si usa esta idea, su nuevo reto será que algunos estudiantes modifiquen AssignmentFactory para que se ajuste a su clase de Asignación (rompiendo su progtwig de prueba).

 package assignment ; public class AssignmentFactory { public AssignmentFactory ( ) { super ( ) ; } public AssignmentFactory make ( .... parameters ) { return new Assignment ( .... arguments ) ; } }