Lahzey
Lahzey

Reputation: 403

java.lang.InstantiationError when creating instance of static inner class with Objenesis

I am trying to create a utility method that should be able to deep-clone any object. (Object.clone() only works on Object implementing Cloneable and I heard it's flawed anyways.)

I am using Objenesis to create new instances of objects without the use of constructors.

However, when trying to clone a JFrame I get the following Exception:
(using this class because I think it should be a good and complex test)

java.lang.InstantiationError: [Ljava.util.concurrent.ConcurrentHashMap$Node;
    at sun.reflect.GeneratedSerializationConstructorAccessor12.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
    at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)

I am open to any solution, not necessarily limited to Objenesis.

My Code:

private static ObjenesisStd OBJENESIS = new ObjenesisStd();

@SuppressWarnings("unchecked")
public static <T> T clone(T object, boolean deep){
    if(object == null){
        return null;
    }else{
        try {
            T clone = (T) OBJENESIS.newInstance(object.getClass());
            List<Field> fields = ReflectionUtil.getAllFieldsInHierarchy(object.getClass());
            for(Field field : fields){
                boolean isAccessible = field.isAccessible();
                boolean isFinal = ReflectionUtil.isFinal(field);
                field.setAccessible(true);
                ReflectionUtil.setFinal(field, false);
                Class<?> type = field.getType();
                if(!deep || type.isPrimitive() || type == String.class){
                    field.set(clone, field.get(object));
                }else{
                    field.set(clone, clone(field.get(object), true));
                }
                field.setAccessible(isAccessible);
                ReflectionUtil.setFinal(field, isFinal);
            }
            return clone;
        } catch (Throwable e) {
            e.printStackTrace();
            //throw new RuntimeException("Failed to clone object of type " + object.getClass(), e);
            return null;
        }
    }
}


public static void main(String[] args) {
    GetterSetterAccess access = new GetterSetterAccess(JFrame.class);
    JFrame frame = new JFrame("Test Frame");
    for(String attr : access.getAttributes()){
        System.out.println(attr + " " + access.getValue(frame, attr));
    }

    System.out.println("----------------------------------------------");
    frame = clone(frame, true);


    for(String attr : access.getAttributes()){
        System.out.println(attr + " " + access.getValue(frame, attr));
    }
}

EDIT: Got it to work with the accepted answer and a few more fixes:

Upvotes: 0

Views: 4822

Answers (1)

Henri
Henri

Reputation: 5731

I finally figured it out. Your code doesn't handle arrays. So it fails with instantiating "[Ljava.util.concurrent.ConcurrentHashMap$Node;" which is an array of Nodes.

However, I will advocate that indeed, you should not do that. You will end up with fairly complicated code. Depending on what you want to do, you could use Jackson or XStream to do a marshall / unmarshall to perform the copy.

If you really want to continue that path, you will need something like this after the null check of your clone method.

    if(object.getClass().isArray()) {
        int length = Array.getLength(object);
        Object array = Array.newInstance(object.getClass().getComponentType(), length);
        for (int i = 0; i < length; i++) {
            Array.set(array, i, clone(Array.get(object, i), true));
        }
        return (T) array;
    }

Upvotes: 2

Related Questions