Is it possible in java to create 'blank' instance of class without no-arg constructor using reflection?
Asked Answered
B

6

42

I have a class which has not default constructor. And I need a way to get 'blank' instance of this class. 'blank' means that after instantiation all class fields should has default values like null, 0 etc.

I'm asking because I need to be able serialize/desirialize big tree of objects. And I have no access to sources of this objects classes and classes has neither default constructors nor implements serializable. It is likely not very good idea to try to serialize such structure but the alternative is to convert it to something more easily serializable.

Beverie answered 9/11, 2010 at 12:37 Comment(0)
S
35

With standard reflection, no, but there is a library that can do it for you: objenesis.

It's specifically designed to instantiate classes without default constructors, and it's used by other serialization libraries like xstream.

Note: the constructor might not be called in these cases (but that's presumably what you want).

Stepparent answered 9/11, 2010 at 12:46 Comment(4)
I agree - very evil. When a programmer creates no ctor they he/she expects that nobody calls it, and - surprise! :-)Powys
But from the other side, "normal" reflection also breaks the rules of class design (like accessing private fields), but at least most programmers realize, that such thing like reflection exists. Very few programmers know about objensis.Powys
Not particularly evil -- very useful in implementing custom serialization schemes, particularly when the original object isn't implemented as serializable. .NET has a method, FormatterServices.GetUninitializedObject(), for just this purpose. I wish Java had something similar that was "official."Pluviometer
yes not particularly evil, but it can be used to break at least one of the guarantees of Java: jqno.nl/post/2015/02/28/hacking-java-enumsDamali
B
25

Having Class instance provided as variable clazz:

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

as described in http://www.javaspecialists.eu/archive/Issue175.html

Blesbok answered 9/11, 2010 at 12:51 Comment(5)
Nice. We might have a winner here! (+1)Globose
Keep in mind though that the ReflectionFactory class is a Sun class so this will only work on Sun/Oracle VMs, and even then it's subject to change.Phraseology
Where does parent come from?Travis
This should by far be the accepted answer. I also have the same question (as so many other mothers)... who is/provides the parent...? or is it a list of calling newConstructorForSerlialization all the way up to a no arg constructor class either serializable or not? and then just filling up the instantiated classes with the serialized data?Ternion
Regarding parent - I think the idea is to "skip" the inaccessible c'tor and use one of the super classes' c'tor instead - so as much of the object fields will be initialized properly. As per @jonstok's answer, you can even just use Object's default c'tor. The main think that I worry about here - fields that the provided c'tor don't know about, are they zero-initialized?Bell
J
8

Your solution will be JVM specific.

If you need a portable solution use a 3rd party library.

For Sun's JVM v1.5 you can do this:

    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());

The relevant classes in XStream are:

  • com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider
  • com.thoughtworks.xstream.core.JVM;
Jotter answered 9/11, 2010 at 13:2 Comment(1)
If I understand correctly, your code causes Sun's ReflectionFactory to use Object's default constructor in place of MyClass's inaccessible constructors. This seems to work but the question remains - are MyClass fields zero initialized when no constructor code exist that know them? I tried to follow the code for ReflectionFactory but it was somewhat too complex for me.Bell
D
3

The only solution I can think of would be to use a bytecode manipulation library such as javassist to add a default constructor.

Destitution answered 9/11, 2010 at 12:47 Comment(0)
B
2

If your class has no other constructor, then the compiler will create one for you. You might have a no-arg constructor and not realize it.

If you do not write a no-arg constructor, and you include even one constructor that takes an argument, then the compiler will not give you one. Reflection won't help, either: if you try to find a no-arg constructor and there isn't one, what do you expect to happen?

It doesn't sound like you can use Java object serialization using java.lang.Serializable, but that's not your only choice. You can also use XML, or JSON, or prototype buffers, or any other protocol that's convenient.

Brachycephalic answered 9/11, 2010 at 12:46 Comment(2)
+1 for being (almost) the only one who knows what he (and the OP) is talking aboutGlobose
Unsafe wasn't an option in 2010 when this question was posted. It's a JDK 9 feature: javaworld.com/article/2952869/java-platform/…Brachycephalic
D
0

If it's a class that implements the java.io.Serializable interface or java.io.Externalizable interface, it's possible by using the same mechanism that Java uses when deserializing objects:

ObjectStreamClass objectStreamClass = ObjectStreamClass.lookup(MyClass.class);
Method newInstance = ObjectStreamClass.class.getDeclaredMethod("newInstance");
newInstance.setAccessible(true);
Object instance = newInstance.invoke(objectStreamClass);
Dicentra answered 5/1 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.