简体   繁体   中英

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:

  • Avoided cloning Wrappers of Primitive Types ( Integer.class etc.)
  • Avoided cloning Classes (Objects of the class Class.class )
  • Stored the cloned objects in a Map and reused them, so if Object A has a reference to Object B and Object B one to Object A it doesn't get stuck in an infinite loop. I also used a Map that checks for exact equality ( == ) instead of using equals() .
  • Created a custom exception class which would just be passed on instead of throwing a new exception on every level (causing a huge caused-by-depth).

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;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM