26

Tengo una clase que no tiene constructor predeterminado. Y necesito una forma de obtener una instancia 'en blanco' de esta clase. 'en blanco' significa que después de la creación de instancias todos los campos de clase deben tener valores predeterminados como nulo, 0 etc.¿Es posible en java crear una instancia 'en blanco' de clase sin constructor sin arg usando reflexión?

Estoy preguntando porque necesito poder serializar/desirializar un gran árbol de objetos. Y no tengo acceso a las fuentes de estas clases de objetos y las clases no tienen constructores predeterminados ni implementa serializables. Probablemente no sea una buena idea tratar de serializar dicha estructura, pero la alternativa es convertirla en algo más fácilmente serializable.

Respuesta

22

Con reflexión estándar, no, pero hay una biblioteca que puede hacerlo por usted: objenesis.

Está específicamente diseñado para crear instancias de clases sin constructores por defecto, y es utilizado por otras bibliotecas de serialización como xstream.

Nota: es posible que no se invoque el constructor en estos casos (pero es probable que eso sea lo que desee).

+1

¡Guau! ¡Qué mal pedazo de código! –

+0

Estoy de acuerdo, muy malvado. Cuando un programador no crea un ctor, él/ella espera que nadie lo llame, y - ¡sorpresa! :-) – iirekm

+0

Pero desde el otro lado, la reflexión "normal" también rompe las reglas del diseño de clase (como el acceso a campos privados), pero al menos la mayoría de los programadores se dan cuenta de que existe algo así como la reflexión. Muy pocos programadores saben sobre objensis. – iirekm

2

Si su clase no tiene otro constructor, el compilador creará uno para usted. Puede tener un constructor sin argumentos y no darse cuenta.

Si no escribe un constructor no-arg, e incluye incluso un constructor que toma un argumento, el compilador no le dará uno. La reflexión tampoco ayudará: si intentas encontrar un constructor sin argumentos y no hay ninguno, ¿qué esperas que suceda?

No parece que pueda usar la serialización de objetos Java utilizando java.lang.Serializable, pero esa no es su única opción. También puede usar XML, o JSON, o buffers de prototipo, o cualquier otro protocolo que sea conveniente.

+0

1 por ser (casi) el único que sabe lo que él (y el OP) está hablando de –

+0

sólo tiene que utilizar 'Unsafe' ... – Enerccio

+0

inseguro no era una opción en 2010, cuando esta cuestión fue publicada . Es una función de JDK 9: https://www.javaworld.com/article/2952869/java-platform/understanding-sun-misc-unsafe.html – duffymo

3

La única solución que se me ocurre sería usar una biblioteca de manipulación de códigos de bytes como javassist para agregar un constructor predeterminado.

+1

¿Por qué el voto a favor? La solución es algo loca, pero válida. –

+1

@seanizer Gracias –

-3

Pruébelo: vea si suObject.class.newInstace() funciona. Si no, todo lo que puedo pensar es crear un objeto prototipo llamando al constructor habitual, establecer sus campos en cero y luego clonarlo.

12

Tener instancia de clase proporcionada como clazz variable:

ReflectionFactory rf = ReflectionFactory.getReflectionFactory(); 
Constructor objDef = parent.getDeclaredConstructor(); 
Constructor intConstr = rf.newConstructorForSerialization(clazz, objDef); 
clazz.cast(intConstr.newInstance()); 

como se describe en http://www.javaspecialists.eu/archive/Issue175.html

+0

Nice. ¡Podríamos tener un ganador aquí! (+1) –

+1

Tenga en cuenta que la clase ReflectionFactory es una clase Sun, por lo que solo funcionará en las máquinas virtuales Sun/Oracle, e incluso entonces está sujeta a cambios. –

+0

¿De dónde viene 'parent'? –

5

Su solución será JVM específica.

Si necesita una solución portátil, use una biblioteca de terceros.

Para v1.5 JVM de Sun puede hacer esto:

final Class<?> myClass = MyClass.class; 
    final ReflectionFactory reflection = ReflectionFactory.getReflectionFactory(); 
    final Constructor<Object> constructor = 
     reflection.newConstructorForSerialization(
      myClass, Object.class.getDeclaredConstructor(new Class[0])); 
    final Object o = constructor.newInstance(new Object[0]); 

    System.out.print(o.getClass()); 

Las clases pertinentes en xstream son:

  • com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider
  • com .thoughtworks.xstream.core.JVM;
Cuestiones relacionadas