Objekte per Reflexion instanziieren

Diskutiere Objekte per Reflexion instanziieren im Allgemeine Java-Themen Bereich.
W

White_Fox

Guten Abend allerseits

Folgende Probleme: Serialisierung macht Ärger, wenn man eine Klasse ändert, JSON zickt bei Zirkelreferenzen herum.

Und nun will ich eine Klasse schreiben, um diese Probleme zu erschlagen. Der Plan ist, die wesentlichen Informationen, die ein Objekt enthält, zu entnehmen und in einer Map zu speichern: Klassenname, Membervariablen und der Hash des Objekts um es eindeutig zu identifizieren. Wenn eine Membervariable ein Objekt ist, wird dessen Hash gespeichert und dieses Objekt ebenso zerlegt.

Aber irgendwie komme ich nicht weiter. Hier ist der Test:

Java:
public class UniversalDataTransferTest {

    //This class simulates a circular object reference
    class ClassToAnalyze {

        private int i;
        private String s;
        private ClassToAnalyze child;
        private ClassToAnalyze parent;

        public ClassToAnalyze() {
            this.i = 0;
            this.s = "TreeLevel " + this.i;
            this.parent = null;
            this.child = new ClassToAnalyze(this.i, this);
        }

        private ClassToAnalyze(int i, ClassToAnalyze parent) {
            this.i = ++i;
            this.s = "TreeLevel " + this.i;
            this.parent = parent;
            this.s = "TreeLevel " + this.i;

            this.child = this.i < 10 ? new ClassToAnalyze(this.i, this) : null;
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof ClassToAnalyze)) {
                return false;
            }

            ClassToAnalyze c = (ClassToAnalyze) obj;
            return this.i == c.i
                    && this.s.equals(c.s)
                    && this.child.equals(c.child);
        }
    }

    /**
     * Test of rebuild method, of class UniversalDataTransfer.
     */
    @Test
    public void testRebuild() {
        UniversalDataTransfer<ClassToAnalyze> instance;
        ClassToAnalyze strippedUpObject, rebuildObject;

        System.out.println("rebuild");

        strippedUpObject = new ClassToAnalyze();
        try {
            instance = new UniversalDataTransfer(strippedUpObject);
            rebuildObject = instance.rebuild();
            assertEquals(strippedUpObject, rebuildObject);
        }
        catch (IllegalAccessException | ObjectParseException | ClassNotFoundException | InstantiationException ex) {
            fail("Execption: " + ex.getClass().getName() + ": " + ex.getMessage());
        }
    }

    /**
     * Test of analyseObject method, of class UniversalDataTransfer.
     */
    @Test
    public void testAnalyseObject() {
        UniversalDataTransfer<ClassToAnalyze> instance;
        ClassToAnalyze strippedUpObject, rebuildObject;

        System.out.println("analyseObject");

        strippedUpObject = new ClassToAnalyze();
        instance = new UniversalDataTransfer();
        try {
            instance.analyseObject(strippedUpObject);
            rebuildObject = instance.rebuild();
            assertEquals(strippedUpObject, rebuildObject);
        }
        catch (IllegalAccessException | ObjectParseException | ClassNotFoundException | InstantiationException ex) {
            fail("Execption: " + ex.getClass().getName() + ": " + ex.getMessage());
        }
    }
}


Die Klasse(n):
Java:
class ObjectDescriptor {
    public String classType;
    public Integer objectHash;
    public ArrayList<MemberfieldDescriptor> fieldDescriptors;
   
    public ObjectDescriptor(){
        fieldDescriptors = new ArrayList<>();
    }
}

class MemberfieldDescriptor {
    public final String name;
    public final Object value;
    public final MemberType type;

    public MemberfieldDescriptor(String name, Object value, MemberType type) {
        this.name = name;
        this.value = value;
        this.type = type;
    }
}

public class UniversalDataTransfer<T> {

    private HashMap<Integer, ObjectDescriptor> objectDescriptions;
    private HashMap<Integer, Object> broughtToBeing;
    private Integer mainObjectHash;

    public UniversalDataTransfer() {

    }

    public UniversalDataTransfer(T o) throws IllegalAccessException {
        analyseObject(o);
    }

    public void analyseObject(T o) throws IllegalAccessException {
        this.objectDescriptions = new HashMap<>();
        this.mainObjectHash = o.hashCode();
        this.stripDown(o);
    }

    public T rebuild() throws ObjectParseException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        broughtToBeing = new HashMap<>();
        return (T) fitUp(objectDescriptions.get(mainObjectHash));
    }

    private void stripDown(Object o) throws IllegalAccessException {
        ObjectDescriptor od;

        if (objectDescriptions.containsKey(o.hashCode())) {
            return;
        }

        od = new ObjectDescriptor();
        od.classType = o.getClass().getTypeName();
        od.objectHash = o.hashCode();
        objectDescriptions.put(od.objectHash, od);

        for (Field field : o.getClass().getDeclaredFields()) {
            MemberfieldDescriptor fd;

            field.setAccessible(true);
            fd = getFieldDescriptor(field, o);
            od.fieldDescriptors.add(fd);
            if (fd.type == MemberType.OBJECT) {
                if (fd.value != null) {
                    stripDown(field.get(o));
                }
            }
        }
    }

    private Object fitUp(ObjectDescriptor od) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class cl;
        Object o;

        if (broughtToBeing.containsKey(od.objectHash)) {
            return broughtToBeing.get(od.objectHash);
        }

        cl = Class.forName(od.classType);
        o = cl.newInstance();                      // <- Hier gibt es Ärger
        broughtToBeing.put(od.objectHash, o);

        for (MemberfieldDescriptor md : od.fieldDescriptors) {
            restorMemberfield(md, o);
        }
        return o;
    }

    private MemberfieldDescriptor getFieldDescriptor(Field field, Object o) throws IllegalArgumentException, IllegalAccessException {
        String name;
        Object value;
        MemberType type;

        name = field.getName();
        type = getMembertype(field.get(o));
        if (field.get(o) != null) {
            value = (type == MemberType.OBJECT) ? field.get(o).hashCode() : field.get(o);
        }
        else {
            value = null;
        }

        return new MemberfieldDescriptor(name, value, type);
    }

    private MemberType getMembertype(Object o) {
        if (o == null) {
            return MemberType.OBJECT;
        }
        for (MemberType mt : MemberType.values()) {
            if (o.getClass().equals(mt.getAssociatedClass())) {
                return mt;
            }
        }
        return MemberType.OBJECT;
    }

    private void restorMemberfield(MemberfieldDescriptor md, Object o) throws IllegalArgumentException, IllegalAccessException, ClassNotFoundException, InstantiationException {
        for (Field f : o.getClass().getDeclaredFields()) {
            if (f.getName().equals(md.name)) {
                f.setAccessible(true);
                if (md.type == MemberType.OBJECT) {
                    if (broughtToBeing.containsKey(md.value)) {
                        f.set(o, broughtToBeing.get(md.value));
                    }
                    else {
                        f.set(o, fitUp(objectDescriptions.get(md.value)));
                    }
                }
                else {
                    f.set(o, md.value);
                }
            }
        }
    }
}

Allerdings bekomme ich an der markierten Stelle ständig eine InstantiationException. Hat jemad eine Ahnung, warum? Ich hab die innere Testklasse im Verdacht, aber in einem kurzen Test habe ich das auch gemacht und da hat das funktioniert.

Edit:
Wenn jemand Vorschläge für Codeverbesserungen hat - immer her damit, ich bin damit noch nicht so richtig zufrieden, diese Ansammlungen verschachtelter If-Statements sehen nicht gerade schön aus.
 
A

abc66

Java:
	public static Object instantiate(Class<?> cl) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		Constructor<?>[] constructors = cl.getConstructors();
		for (Constructor<?> constructor : constructors) {
			if (constructor.getParameterCount() == 0) {
				return constructor.newInstance(new Object[0]);
			}
		}
		return null;
	}
.newInstance(); is deprecated und Du hast new Object[0] vergessen.
 
W

White_Fox

.newInstance(); is deprecated
Hm, in Java 8 anscheinend noch nicht. Aber danke dafür. Eine Merkwürdigkeit ist da aber noch drin: Ich finde in cl.getConstructors() keinen Konstruktor, der ein getParameterCount() == 0 zurückliefert. Sondern es wird 1 zurückgegeben. Die Methode getDeclaredConstructors() liefert noch einen, der drei Parameter verlangt, aber der andere Konstruktor benötigt nur zwei.

Irgendeine Idee, was da passiert?
 
W

White_Fox

Sicher. Aber das erkärt mir noch nicht, warum ich ihn nicht aufrufen kann bzw. warum der nach einem Parameter verlangt, den ich nicht deklariert habe.
 
W

White_Fox

Es lag doch daran, daß das Testobjekt über eine innere Klasse gebaut wird und ich das nicht berücksichtigt habe. Jetzt muß ich darauf auch noch achten...wenn es insgesamt mal läuft.
 
W

White_Fox

So, da ich mal wieder Zeit (aber vor allem Lust) habe, mich damit weiterzubefassen: Innere Klassen instanzieren, weiter gehts.

Bei tutorialspoint.com habe ich dieses Beispiel:
Java:
class OuterClass {
   private int value = 20;
      class InnerClass {
         void show() {
            System.out.println("Value is: "+ value);
      }
   }
}
public class Test {
   public static void main(String args[]) {
      OuterClass obj = new OuterClass();
      OuterClass.InnerClass in = obj.new InnerClass();
      in.show();
   }
}
Wie mache ich das aber, wenn ich die Klassennamen zur Programmierzeit gar nicht kenne/kennen will?
 
Wurstkopp

Wurstkopp


Java:
          Class<?> enclosingClass = Class.forName("OuterClass");
          Object enclosingInstance = enclosingClass.getDeclaredConstructor().newInstance();

          Class<?> innerClass = Class.forName("OuterClass$InnerClass");
          Constructor<?> ctor = innerClass.getDeclaredConstructor(enclosingClass);

          Object innerInstance = ctor.newInstance(enclosingInstance);
          
          ((OuterClass.InnerClass) innerInstance).show();
 
Thema: 

Objekte per Reflexion instanziieren

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben