Reflection gone wrong?

berndoa

Top Contributor
Hallo,
ich habe folgende Klassen:
Java:
import java.lang.reflect.Field;

public class Initializer {

    public void setup() {
        try {
            String thisClassName = this.getClass().getSimpleName();
            String otherClassName = "";
            Field[] thisfields = this.getClass().getDeclaredFields();

            for (Field thisfield : thisfields) {
                Field[] otherfields = thisfield.getType().getDeclaredFields();

                for (Field otherfield : otherfields) {
                    otherClassName = otherfield.getType().getSimpleName();
                    System.out.println("thisname=" + thisClassName + "; otherClassName=" + otherClassName);

                    if (thisClassName == otherClassName) {
                        otherfield.set(thisfield, this);
                    }
                }

            }

        } catch (Exception e) {
        }

    }

}



Java:
public class GameflowManager extends Initializer {

    LoginManager loginmanager;
    MaingameManager maingamemanager;
    ExitManager exitmanager;

    public static void main(String[] args) {
        new GameflowManager();
    }

    public GameflowManager() {
        setup();
        System.out.println(loginmanager+"");
    }

}

Java:
public class LoginManager extends Initializer {

    GameflowManager gameflowmanager;

    public LoginManager() {
    }

}

Was ich eigentlich tun wollte bzw. will:
Alle Klasssen meines Projekts erben von der Initializer Klasse, haben also die setup() Methode zur Verfügung.
Was die Setup Methode tun soll:
1. alle Fields der Klasse raussuchen
2. denen ein neues Objekt der betreffenden Klasse zuordnen.
Also bspw.
Code:
loginmanager=new LoginManager();

3. in die so initializierten Objekte reingehen,
4. in jedem davon die Fields angucken und gucken ob es klassenmässig gleich dem Ursprungsobjekt ist
5. Referenz in die Rückrichtung einrichten


Also im Prinzip diesen Code durchführen
Code:
loginmanager=new LoginManager();
loginmanager.gameflowmanager=this;
Gleiches für extimanager und maingamemanager.


Reflection deshalb damit ich das nicht für jedes Feld extra hardcoden muss, sondern das halt "von selsbt" die vorhandenen Felder erkennt, initializiert und die hin und rückverbindung herstellt.


Habe den oben genannten Code.
Nur fehlt mir der Part wie man die Felder der klasse initializiert (also wie das =new ...(); hinkriegt).
Ob der Rest überhaupt richtig ist oder funktioniert, kann ich auch nicht beurteilen.

Kann mir da Jemand weiterhelfen?

Und ja, ich weiß, ich käme ohne Reflection aus.

Aber ich würde es gerne mal lauffähig mit Reflection hinkriegen :)
 

KonradN

Super-Moderator
Mitarbeiter
if (thisClassName == otherClassName)
String vergleicht man nicht mit == sondern mit equals (Mag hier evtl. gehen, da es auf den Typ ankommt und wenn der SimpleName als String da fest gespeichert ist, dann wäre die Referenz gleich. Hier den SimpleName zu verwenden ist aber auch nicht gut. Der SimpleName kann durchaus gleich sein, ohne dass die Klassen gleich sind. Du könntest also einfach die Klassen selbst vergleichen - die sollten dann in der Regel auch gleich sein von der Referenz.


Die Logik Deiner setup Routine leuchtet mir aber auch noch nicht wirklich ein. Wenn ich auf einer Instanz setup aufrufe, dann wäre meine Erwartung, dass eben diese Klasse initialisiert würde. Aber das hast Du ja nicht wenn ich das richtig verstanden habe.

Daher wäre es super, wenn Du erst einmal überlegst, was genau wie ablaufen soll. Das kannst Du dann beschreiben und dann kann man Dir auch im Detail bei der Umsetzung helfen. Aber hier ist es schwer, auch nur im Detail heraus zu kriegen, was Du überhaupt vor hast.

Wenn Du den LoginManager erzeugst, dann ist die Frage: Woher kommt denn der GameManager in der Klasse? So wie ich das jetzt etwas verstehe, was Du vorhast, hast Du sowas wie ein Henne / Ei Problem.

Wenn Du schon so eine Sache aufbauen willst, dann schau doch erst einmal, was es für "übliche Abläufe" gibt. Das hatte ich Dir in einem anderen Thread schon einmal etwas beschrieben, wie so eine "Dependency Injection" aufgebaut werden könnte. Sowas könnte in die Richtung gehen, was Du eigentlich willst.

Dann hast Du eine Stelle, die die Hauptverantwortung hat. Die ist verantwortlich, diese "verwalteten" Instanzen zu erzeugen. Du hast also eine Klasse Injektor. Die kann dann Klassen erstellen.

Also Du sagst dann dem Injektor: Ich hätte gerne eine Instanz von GameManager. Also schaut der Injektor: Habe ich da schon eine Instanz?
Wenn nicht:
  • Dann schaut sich der Injektor die Konstuktoren an. Wenn möglich wird ein Konstruktor genutzt um dann eine Instanz zu erstellen.
  • Nach der Erstellung wird die Instanz dann untersucht. Wenn der GameManager ein Feld "LoginManager" hat, das eine Annotation hat, die Du dafür vorgesehen hast (@Autowired, @Inject oder was auch immer), dann wird dieses Feld gesetzt. Dazu wird die Instanz so gesucht, wie schon beschrieben (also sozusagen ein rekursiver Aufruf.)

Dann hast Du auch Deine Spielerei mit Reflection.
 

berndoa

Top Contributor
Ich habe ein wenig am Code rumgewerkelt:

Java:
import java.lang.reflect.Field;

public class Initializer {

    public void setup() {
        try {

            String thisClassName = this.getClass().getSimpleName();
            String otherClassName = "";
            Field[] thisfields = this.getClass().getDeclaredFields();

            for (Field thisfield : thisfields) {
                if (thisfield.get(this) != null) {
                    continue;
                }
                thisfield.set(this, thisfield.getType().getDeclaredConstructor().newInstance());
                Field[] otherfields = thisfield.getType().getDeclaredFields();

                for (Field otherfield : otherfields) {
                    if (otherfield.get(thisfield.get(this)) != null) {
                        continue;
                    }
                    otherClassName = otherfield.getType().getSimpleName();
                    System.out.println("thisname=" + thisClassName + "; otherClassName=" + otherClassName);

                    if (thisClassName == otherClassName) {
                        otherfield.set(thisfield.get(this), this);
                    }
                }

            }

        } catch (Exception e) {
        }

    }

}

Nun scheint sich zumindest etwas zu tun, nur rufen sich nun die in den objekten enthaltenen setup methoden durch den kostruktor gegenseitig auf.

Um zu verhindern dass wir hier einen endlosen aufrufzyklus haben, wollte ich eigentlich erreichen dass, falls die betrachtenen Felder shcon auf irgendwas festgelegt wurden (also nicht null sind), dass sie geskipt werden.

Sodass nur die noch nicht zugewiesenen felder bearbeitet werden.

Leider erreichen die eingesetzten if vergleiche offenbar nicht ihre wirkung, da trotzdem das endlos hin und hergeht :-/
 

berndoa

Top Contributor
if (thisClassName == otherClassName)
String vergleicht man nicht mit == sondern mit equals (Mag hier evtl. gehen, da es auf den Typ ankommt und wenn der SimpleName als String da fest gespeichert ist, dann wäre die Referenz gleich. Hier den SimpleName zu verwenden ist aber auch nicht gut. Der SimpleName kann durchaus gleich sein, ohne dass die Klassen gleich sind. Du könntest also einfach die Klassen selbst vergleichen - die sollten dann in der Regel auch gleich sein von der Referenz.


Die Logik Deiner setup Routine leuchtet mir aber auch noch nicht wirklich ein. Wenn ich auf einer Instanz setup aufrufe, dann wäre meine Erwartung, dass eben diese Klasse initialisiert würde. Aber das hast Du ja nicht wenn ich das richtig verstanden habe.

Daher wäre es super, wenn Du erst einmal überlegst, was genau wie ablaufen soll. Das kannst Du dann beschreiben und dann kann man Dir auch im Detail bei der Umsetzung helfen. Aber hier ist es schwer, auch nur im Detail heraus zu kriegen, was Du überhaupt vor hast.

Wenn Du den LoginManager erzeugst, dann ist die Frage: Woher kommt denn der GameManager in der Klasse? So wie ich das jetzt etwas verstehe, was Du vorhast, hast Du sowas wie ein Henne / Ei Problem.

Wenn Du schon so eine Sache aufbauen willst, dann schau doch erst einmal, was es für "übliche Abläufe" gibt. Das hatte ich Dir in einem anderen Thread schon einmal etwas beschrieben, wie so eine "Dependency Injection" aufgebaut werden könnte. Sowas könnte in die Richtung gehen, was Du eigentlich willst.

Dann hast Du eine Stelle, die die Hauptverantwortung hat. Die ist verantwortlich, diese "verwalteten" Instanzen zu erzeugen. Du hast also eine Klasse Injektor. Die kann dann Klassen erstellen.

Also Du sagst dann dem Injektor: Ich hätte gerne eine Instanz von GameManager. Also schaut der Injektor: Habe ich da schon eine Instanz?
Wenn nicht:
  • Dann schaut sich der Injektor die Konstuktoren an. Wenn möglich wird ein Konstruktor genutzt um dann eine Instanz zu erstellen.
  • Nach der Erstellung wird die Instanz dann untersucht. Wenn der GameManager ein Feld "LoginManager" hat, das eine Annotation hat, die Du dafür vorgesehen hast (@Autowired, @Inject oder was auch immer), dann wird dieses Feld gesetzt. Dazu wird die Instanz so gesucht, wie schon beschrieben (also sozusagen ein rekursiver Aufruf.)

Dann hast Du auch Deine Spielerei mit Reflection.
Naja, nennen wir die Klassen jetzt einfach mal A und B.
Beide erben von der Initializerklasse, können also die Setup Methode benutzen.

Dann konzeptual sollte das Ganze so in etwa aussehen:

Java:
public class A{
    B b;

    public static void main(String[] args){
        new A();
    }

    public A(){
        setup();
    }

}


Code:
public class B{
    A a;


    public B(){
        setup();
    }

}

A ist also die Starterklasse sozusagen.
Es wird die Mainmethode aufgerufen, dadurch wird ein A objekt erzeugt.
Damit haben wir das erste Objekt a, das ein Feld vom Typ B hat (anfangs uninitialisiert).
Dann wird, im Rahmen des Kosntruktors, setup() aufgerufen.

Dieses guckt nun in a sich um, findet den "B b" Part.
initialisiert diesen erst mit b=new B();
dann geht er in b rein und guckst sich in dessen Feldern um.

in b findet dort "A a" und setzt "a=this;" (denn shcließlich wurde setup() ja ursprünglich von a aus aufgerufen).


Kurzum, erst wird eine verbindung von a zu b gesetzt, wobei hier b wenn nötig eben neu erzeugt wird.
und dann wird ebenjene verbindung a-> b genutzt um in b eine verbindung b->a zu setzen.

so wie man bei doppelt verketteten listen mit einem element erst von der wurzel zum element verweisen kann.
und dann über diesen verbindung von der node zur wurzel als vorgänger verweisen kann.

Keine Ahnung ob es aktuell das tut, was ich mir erhoffe.
Aber problem aktuell ist definitiv dass, wenn a b "erzeugt", der aufgerufene Konstrukotr von b wieder in b die setup methode triggert und das endlos hin und her geht.
also a.setup b.setup waufruft und zurück.

versuche ich durch die null vergleiche zu verhindern, tut es shceinbar aber noch nicht :-/
 

KonradN

Super-Moderator
Mitarbeiter
Nur noch ein anderer Gedankengang:

Das, was dieses "setup" macht: Gehört das wirklich zu dem Verhalten der jeweiligen Klassen?

Wie sieht es denn in der echten Welt aus? Hat ein Auto da das Wissen, woher wie die einzelnen Bauteile kommen? Oder wo steckt dieses Wissen?

Immer, wenn man eine Logik an eine Stelle platziert, wo es eigentlich nicht hin gehört, dann wird es oft unnötig kompliziert und schwer. Diese Problematik mit dem gegenseitigen Aufruf und das landen in so einer Schleife, ist da ein gutes Beispiel für.

Sobald dies von außen gesteuert wird, wird es deutlich einfacher - dann kann man sauber speichern, was man schon alles an Instanzen hat. Und dann nimmt man dies immer.

Das wäre übrigens ein Vorgehen, das man hier auch wählen könnte. Dann bekommt die Methode z.B. eine Map<String, Object> als Parameter wo Du Klassenname -> Instanz Paarevorhalten kannst. Und dieser Parameter muss dann in Setup übergeben werden. Da Setup aus dem Konstruktor aufgerufen wird, muss es einen zweiten Konstruktor mit diesem Parameter geben.
Der Konstruktor ohne Parameter ruft also den Konstruktor mit new ConcurrentHashMap<String, Object>() auf. Und der Konstruktor packt sich immer selbst da mit hinein. Dann kannst Du die Felder durchgehen und immer prüfen: Gibt es noch keine Instanz in der Map? Dann wird diese angelegt (und landet dann in der Map). Und dann wird die Instanz aus der Map zugewiesen.

Der Ablauf geht also aber es wird deutlich: Es gibt einen State (in der Map) und viele Logik. Und diese ist etwas Separates, das zu den eigentlichen Klassen eben nicht dazu gehört. ==> Daher ist dies durchaus separat zu halten. (Was dann auch etwas erklärt, wieso das bei den vorhandenen Lösungen auch so ist.)
 

Neumi5694

Top Contributor
Besser: Jede Sub-Klasse kümmert sich selbst um ihre Felder. Woher soll denn die Super-Klasse wissen, womit welches Feld initialisiert werden soll?
Was, wenn z.B. ein Datentyp keinen Default-Konstruktor hat? Was macht die dann?

Warum du das nicht einfach im Konstruktor machst, ist mir eh ein Rätsel, dafür ist er schließlich da. Da braucht's keine eigene Setup-Methode.
 

berndoa

Top Contributor
Fragen wir mal anders:
Graphbild.png


So oder ähnlich könnten die am Ende zu exisiterenden Objekte (als Knoten dargestellt) und ihre gegenseitigen Verbindungen (als Kanten dargestellt) aussehen.
Es existieren keine 2 Objekte, die die selbe Klasse besitzen (wozu auch, jede Klasse tut ja wa Anderes)
Irgendwo sitzt das anfangs mit main Methode erzeugte Objekt der Klasse A, das "Ursprungsobjekt".
Dies hat, über die darin stehenden Felder, die benötigten "ausgehenden" Kanten im Hinterkopf.
erzeugt also alle mit sich verbundenen Objekte und fügt in diesen eine Referenz auf sich selbst ein.
so werden von A aus alle Objekte/Knoten mit Abstand 1 erzeugt.
Von jenen Knoten aus werden die direkten Partnerknoten erzeugt und verlinkt, also indirekt die Knoten mit Abstand 2 von A erzeugt.
usw bis Alle Knoten und Verbindungen da sind.
Man denke da an das Prinzip der Breitensuche, nur dass da in jeder "Runde" Knoten mit immer größerem Abstand vom ursprungsknoten erzeugt werden.

Warum nicht in JEDER EINZELNEN KLASSE das Erzeugung jeder neuen Instanz für jedes der Felder hardcoden?
Mal vom "Pain in the Ass" abgesehen, soll zum Kompilierungs/Ausführungszeitpunkt sich der ganze Graph "selbst aufbauen" und es muss nur der erste Knoten direkt erzeugt werden.

Durhc die Klassendifinitionen und ihre Felder ist ja klar was womit wie zu verbinden ist (es handelt sich übringens immer um beidseitige Verbindungen).

Also warum händisch wernns auch das Programm erledigen kann?

Und ausserdem ist ja mein programm noch lange nicht fertig.

Vielleicht klatsche ich hier, da und dort noch (sinnbildlich) einen Knoten hinein, der aber dann mit den Knoten 3,7,35,69,und 90 in Verbindung stehen soll.

Ich muss dann schon in jeder der betroffenen Klassen entsprechend nicht initialisierte Felder ("A a") reinbauen, da will ich nciht noch überall entsprechende Instanzerzeugungszeilen einfügen müssen.

Falls ich mehr in Reflection durchblicke, gebe ich dem Programm sogar vielleicht irgendwie vor, welche Klassen mit welchen zu verbinden sind (aka welche Knoten und welche Verbindungen gibt es) und das Programm baut zu einer leeren Klasse die nötigen Felder ein oder so. Keine Ahnung.

@

Neumi5694
Ich sehe die fest vorgegebenen Felder als eine Art Richtungspfeil.
hat die Klasse A Felder wie "B b","C c",
dann soll es eben ein Objekt b der Klasse b und ein Objekt c der Klasse C geben, zu denen A in verbindung steht.
Also As b Feld verweist darauf und die in b vorhandene a referenz verweist zurück.

a zeigt auf b und b auf a.
Gibt eine der hübschen Kanten im obigen Bild.​


Solange ich da bin, hat eine Klasse einen Konstruktor :)

Direkt im Konstruktor ist nervig, weil müsste ich von hand schreiben und habe bei 10 oder 20 Klassen da keine Lsut, von Hand überall
sowas wie

Bash:
this.blabla=blabla;
this.blibli=blibli;
this.blub=blub;
zu schreiben.

kann java ja auch selsbt gucken, Felder mit welchen Klassen da sind und erzeugt ein solches Objekt oder, falls schon existent, verlinkt darauf.

Es ist ja immer dasselbe Prinzip, muss sich doch vereinfachen lassen können.

Und das auslagern in eine initializer Klasse ist rein deshalb damit ich nicht die selben 10 Zeilen Code der setup() Methode in jede der dutzenden Klassen kopieren muss., Redundanz vermeiden halt.
 

berndoa

Top Contributor
Wie sollte es auch? Du rufst setup() ja immer aus einem Konstruktor heraus auf, der nichts initialisiert. Wie sollte da eines der Felder jemals etwas anderes als null enthalten?
Die Zeile 16
Java:
thisfield.set(this, thisfield.getType().getDeclaredConstructor().newInstance());
in

Java:
import java.lang.reflect.Field;

public class Initializer {

    public void setup() {
        try {

            String thisClassName = this.getClass().getSimpleName();
            String otherClassName = "";
            Field[] thisfields = this.getClass().getDeclaredFields();

            for (Field thisfield : thisfields) {
                if (thisfield.get(this) != null) {
                    continue;
                }
                thisfield.set(this, thisfield.getType().getDeclaredConstructor().newInstance());
                Field[] otherfields = thisfield.getType().getDeclaredFields();

                for (Field otherfield : otherfields) {
                    if (otherfield.get(thisfield.get(this)) != null) {
                        continue;
                    }
                    otherClassName = otherfield.getType().getSimpleName();
                    System.out.println("thisname=" + thisClassName + "; otherClassName=" + otherClassName);

                    if (thisClassName == otherClassName) {
                        otherfield.set(thisfield.get(this), this);
                    }
                }

            }

        } catch (Exception e) {
        }

    }

}

sollte das eigentlich tun, also für jedes Feld ein entsprechendes Objekt erzeugen und zuweisen.
 

KonradN

Super-Moderator
Mitarbeiter
@Meniskusschaden wollte aus meiner Sicht darauf hinaus, dass die Felder immer null sind, wenn es in dieses setup() hinein geht. Und damit schließt Du den Kreis.

Wenn A ein B hat und B ein A:
A1 -> erzeugt ein B1.
B1 -> erzeugt ein A2.
A2 -> erzeugt ein B2.
...

Du brauchst also - so wie ich es beschrieben habe - eine Art Gedächtnis: Was habe ich bereits angelegt:

A1 -> Es wird gemerkt: Es gibt eine A Instanz A1. Und dann wird eine Instanz B1 erzeugt.
B1 -> Es wird gemerkt: B Instanz B1 gibt es. Es wird eine A Instanz gebraucht. Und man hat sich ja A1 gemerkt und somit kann A1 genommen werden.
 

KonradN

Super-Moderator
Mitarbeiter
Evtl. willst Du da auch einfach einmal mehr über diese ganze Thematik lesen. Ich denke, dass Inversion of Control hier das wichtige Stichwort ist.

Es wurde bisher auf Dependency Injection als eine Lösungsidee hingewiesen (Weil die sich etwas aus Deinem Ansatz ergibt - das habe ich ja etwas in einer Antwort versucht darzustellen), aber es gibt ja prinzipiell auch andere Ideen, die in dem verlinkten Wikipedia Artikel aufgeführt sind. Da kannst Du also ggf. auch die eine oder andere Anregung oder ein besseres Gesamtverständnis bekommen.
 

Neumi5694

Top Contributor
So wie du's hier beschreibst, kannst du dir die Referenzen auch gleich sparen. Wenn es von jeder Klasse nur eine Instanz gibt und geben soll, kannst du genausogut Singletons verwenden
Java:
AClass.instance()
Aber falls du doch lokale Variablen haben willst, dann wäre das 'händische' Erledigen a) einfacher, würde b) weniger Code brauchen und c) wäre auch wartbar, falls du in einem Jahr nochmal dran musst.

Wenn das Ganze beim Erstellen einer Klasse ausgeführt wird, kriegst du so oder so Probleme, da sie gegenseitig aufeinander verweisen, den Verweis dürfte es aber erst geben, wenn das andere Objekt existiert. Das wäre tatsächlich ein Grund eine setup() Methode nach Erstellen der Klasse auszuführen. Ich würde aber vorschlagen, gleich auf die Variablen zu verzichten.

edit: 2 Gedanken in einem Wort verknüpfen geht selten gut, ist korrigiert.
 
Zuletzt bearbeitet:

berndoa

Top Contributor
So wie du's hier beschreibst, kannst du dir die Referenzen auch gleich sparen. Wenn es von jeder Klasse nur eine Instanz gibt und geben soll, kannst du genausogut Singletons verwenden
Java:
AClass.instance()
Aber falls du doch lokale Variablen haben willst, dann wäre das 'händische' Erledigen a) einfacher, würde b) weniger Code brauchen und c) wäre auch wartbar, falls du in einem Jahr nochmal dran musst.

Wenn das Ganze beim Erstellen einer Klasse ausgeführt wird, kriegst du so oder so Probleme, da sie gegenseitig aufeinander verweisen, den Verweis dürfte es aber erst geben, wenn das andere Objekt existiert. Das wäre tatsächlich ein Grund eine setup() Methode nach Erstellen der Klasse auszuführen. Ich würde aber vorschlagen, gleich auf die Variablen zu verzichten.

edit: 2 Gedanken in einem Wort verknüpfen geht selten gut, ist korrigiert.
Naja, ich denke wenn die Setup Methode ein Objekt und dessen Felder betrachtet, kann es doch eigentlich nur 2 Situationen geben:
Entweder ist das jeweilige Feld unintialisiert, also null (sollte es zumindest sein).
Oder es hat als Wert eine Referenz auf irgendwas, ist also null, und soll dann schlicht nichts tun.
Wobei dieses "Überspringen" nicht zu klappen scheint.

Ja, vermutlich wäre es einfacher, wenn ich über initializervererbung jeder Klasse auch eine generische(?) Methode gäbe a la
Java:
public void set(T t){
//Suche nach dem Field, dessen Wert ein Objekt der Klasse T ist
//ersetze es mit t aus dem Input

}

Dann könnte man in A direkt sowas wie

Java:
//zuerstCode, der b eine neue Instanz der Klasse B zuweist
b.set(this);
 

httpdigest

Top Contributor
Naja, ich denke wenn die Setup Methode ein Objekt und dessen Felder betrachtet, kann es doch eigentlich nur 2 Situationen geben:
Entweder ist das jeweilige Feld unintialisiert, also null (sollte es zumindest sein).
Oder es hat als Wert eine Referenz auf irgendwas, ist also nicht null, und soll dann schlicht nichts tun.
Und genau das ist falsch.
Du rufst setup() ja innerhalb des Konstruktors auf! Wie bereits mehrere Male schon oben gesagt.
Jedes Feld von this wird also immer null sein.
Du startest ja jedesmal mit einem frischen this, wenn du setup() betrittst für die jeweilige neu erzeugte Instanz.
 

Neumi5694

Top Contributor
Naja, ich denke wenn die Setup Methode ein Objekt und dessen Felder betrachtet, kann es doch eigentlich nur 2 Situationen geben:
Entweder ist das jeweilige Feld unintialisiert, also null (sollte es zumindest sein).
Beides ist durch die Verwendung von Singletons bereits abgebildet - viel weniger kompliziert und besser gekapselt.
Sobald zu zugreifst, wird entweder eine neue Instanz erstellt oder eben nicht, falls es schon eine gibt.



Das Problem von sich gegenseitig initialisierenden Klassen, von denen es nur eine Instanz geben darf:

Konstruktor von "A" wird aufgerufen, findet eine Variable (noch null), die auf "B" verweisen soll, erstellt also ein neues "B". In Folge wird der Konstruktor von "B" aufgerufen, der findet eine soll-Referenz auf "A", möchte nun den Konstruktor von "A" aufrufen, aber der läuft ja noch! Es würde nun also ein neues "A" erzeugt, was laut deiner Aussage nicht sein darf.

Deshalb darf setup() erst ausgeführt werden, wenn alle Objekte erzeugt wurden oder du verzichtest eben gleich auf die Referenzen. Wozu hast du die, wenn es doch nur jeweils eine Instanz geben darf? Referenzen sind dann sinnvoll, wenn es mehrere Instanzen geben kann, weil du ja eben die richtige Instanz referenzieren musst.

Dann könnte man in A direkt sowas wie

Java:
//zuerstCode, der b eine neue Instanz der Klasse B zuweist
b.set(this);
Grundsätzlich ja, aber - wie gesagt - falls es nur eine Instanz von A un B geben darf, kannst du das erst machen, wenn alle Konstruktoren abgearbeitet wurden oder aber A kümmert sich um das Erstellen von B, B erstellt niemals ein A.

Wie gesagt ... Singelton, statische Zugriffsmethode auf die einzige Instanz. Damit sind alle deine Probleme gelöst. Du musst das Rad nicht neu erfinden, vor ähnlichen Probleme standen schon andere.
 

berndoa

Top Contributor
Deshalb darf setup() erst ausgeführt werden, wenn alle Objekte erzeugt wurden oder du verzichtest eben gleich auf die Referenzen. Wozu hast du die, wenn es doch nur jeweils eine Instanz geben darf? Referenzen sind dann sinnvoll, wenn es mehrere Instanzen geben kann, weil du ja eben die richtige Instanz referenzieren musst.
Irgendwie verstehe ich die Frage nicht.

Wie soll denn bspw. das a Objekt eine Methode der b Methode aufrufen können, wenn es b gar nicht "kennt", also keine Referenz darauf besitzt?

Ja, vermutlich machen mir Singletons da das Leben wesentlich leichter, man hört doch immr wieder von neuen Sachen wo man gar nicht wusste dass es sie überhaupt gibt :)

Hinsichtlich Konstruktor dachte ich, das Objekt (also this) existiert schon in dem Moment wo der Konstruktor abgearbeitet wird.
Aber gut zu wissen :)
 

KonradN

Super-Moderator
Mitarbeiter
Ja, vermutlich machen mir Singletons da das Leben wesentlich leichter, man hört doch immr wieder von neuen Sachen wo man gar nicht wusste dass es sie überhaupt gibt :)
Das mag zwar so auf den ersten Blick scheinen, aber Singletons bringen auch Probleme mit die man spätestens beim Testen merken wird.

Aber ich habe Dir oft genug verschiedene Dinge vorgeschlagen auf die Du nicht eingegangen bist.... Daher erspare ich mir weitere Erläuterungen.
 

Neumi5694

Top Contributor
Irgendwie verstehe ich die Frage nicht.

Wie soll denn bspw. das a Objekt eine Methode der b Methode aufrufen können, wenn es b gar nicht "kennt", also keine Referenz darauf besitzt?

Ja, vermutlich machen mir Singletons da das Leben wesentlich leichter, man hört doch immr wieder von neuen Sachen wo man gar nicht wusste dass es sie überhaupt gibt :)

Hinsichtlich Konstruktor dachte ich, das Objekt (also this) existiert schon in dem Moment wo der Konstruktor abgearbeitet wird.
Aber gut zu wissen :)
"Singleton" ist nur eine Spezifikation, die in einem Wort beschreibt, was man meint. Kann man auch anwenden, ohne den Begriff zu kennen :)
Wenn es nur eine Instanz geben darf, dann dient dieses Pattern dazu, genau diese Voraussetzung zu gewährleisten. Außerdem ernöglicht es auch den Zugriff ohne Referenz.
Hättest du nachgelesen, worum es sich dabei handelt, dann wärst du über so was gestolpert:
Java:
class A {
    private A() {
        //...
    }

    /**
     * Liefert die einzige Instanz von A
     * @return die Instanz
     */
    public static A getInstance() {
        return instance;
    }
    private final static A instance = new A();

    //Alternativ kann man es auch erst in der Zugriffsmethode bei Bedarf initialisieren, geht auch, dann allerdings ohne final, was eigentlich auch wurscht ist.
}


Ich find dein Konstrukt aus sich gegenseitig referenzierenden Objekten mit jeweils nur einer Instanz einer Klasse eh etwas seltsam. Wofür immer du es auch brauchst, denke ich, dass man das besser lösen kann. Aber wenn du schon so was brauchst, kann man es auch so gestalten, dass es weniger fehleranfällig ist.


Zu deinem letzten Punkt.
Doch, das Objekt existiert dann schon.
Das hier geht problemlos.
Code:
class A {
  public A() {
    this.b = new B(this);
  }
}

class B {
  public B(A a) {
    this.a = a
  }
}
Nur wenn du stattdessen ohne Parameter arbeitest und per im Konstruktor aufgerufener setup Methode in B prüfst, ob this.a gesetzt ist und dann ein neues A() erstellst ... dann hast du zwei, was du ja nicht wolltest. Ich denke sogar, dann hast du einen Stackoverflow (oder eine OutOfMemoryException), da jedes neue A ein neues B erstellt und umgekehrt.
Also so was (ohne Setup Methode, die letztendlich auch nichts anderes macht). Die würden neue Objekte erzeugen, bis der Speicher voll ist.
Code:
  //Bitte nicht zu Hause nachmachen, das geht in die Hose
class A {

  public A() {
    if (this.b == null) {
      this.b = new B();
    }
  }
}
class B {
  public A() {
    if (this.a == null) {
      this.a = new A();
    }
  }
}


Ich stimme @KonradN da übrigens zu, normalerweise gibt es elegantere/bessere Lösungen. Im Normalfall gibt's aber auch mehr als eine erlaubte Instanz. Das ist wirklich nur für den einen Anwendungsfall gedacht und geeignet, wo es wirklich nur eine geben darf.
 
Zuletzt bearbeitet:

berndoa

Top Contributor
"Singleton" ist nur eine Spezifikation, die in einem Wort beschreibt, was man meint. Kann man auch anwenden, ohne den Begriff zu kennen :)
Wenn es nur eine Instanz geben darf, dann dient dieses Pattern dazu, genau diese Voraussetzung zu gewährleisten. Außerdem ernöglicht es auch den Zugriff ohne Referenz.
Hättest du nachgelesen, worum es sich dabei handelt, dann wärst du über so was gestolpert:
Java:
class A {
    private A() {
        //...
    }

    /**
     * Liefert die einzige Instanz von A
     * @return die Instanz
     */
    public static A getInstance() {
        return instance;
    }
    private final static A instance = new A();

    //Alternativ kann man es auch erst in der Zugriffsmethode bei Bedarf initialisieren, geht auch, dann allerdings ohne final, was eigentlich auch wurscht ist.
}


Ich find dein Konstrukt aus sich gegenseitig referenzierenden Objekten mit jeweils nur einer Instanz einer Klasse eh etwas seltsam. Wofür immer du es auch brauchst, denke ich, dass man das besser lösen kann. Aber wenn du schon so was brauchst, kann man es auch so gestalten, dass es weniger fehleranfällig ist.


Zu deinem letzten Punkt.
Doch, das Objekt existiert dann schon.
Das hier geht problemlos.
Code:
class A {
  public A() {
    this.b = new B(this);
  }
}

class B {
  public B(A a) {
    this.a = a
  }
}
Nur wenn du stattdessen ohne Parameter arbeitest und per im Konstruktor aufgerufener setup Methode in B prüfst, ob this.a gesetzt ist und dann ein neues A() erstellst ... dann hast du zwei, was du ja nicht wolltest. Ich denke sogar, dann hast du einen Stackoverflow (oder eine OutOfMemoryException), da jedes neue A ein neues B erstellt und umgekehrt.
Also so was (ohne Setup Methode, die letztendlich auch nichts anderes macht). Die würden neue Objekte erzeugen, bis der Speicher voll ist.
Code:
  //Bitte nicht zu Hause nachmachen, das geht in die Hose
class A {

  public A() {
    if (this.b == null) {
      this.b = new B();
    }
  }
}
class B {
  public A() {
    if (this.a == null) {
      this.a = new A();
    }
  }
}


Ich stimme @KonradN da übrigens zu, normalerweise gibt es elegantere/bessere Lösungen. Im Normalfall gibt's aber auch mehr als eine erlaubte Instanz. Das ist wirklich nur für den einen Anwendungsfall gedacht und geeignet, wo es wirklich nur eine geben darf.
Also vielleicht habe ich nur einen Denkfehler drin:
In meiner Definition der Setup Methode (in der Initializer Klasse) gehe ich ja erst mit einer for each Schleife die eigenen Felder des Objekts durch.
Ist eins der Felder ungleich null (heißt: wurde, wie auch immer, shcon mal mit was ausser null belegt) dann sollte er doch eigentlich durch das

Java:
                if (thisfield.get(this) != null) {
                    continue;
                }
gerade zum nächsten Field übergehen, also das aktuelle Feld völlig ohne Aktion "überspringen".

Gleichermassen, wenn gerade ein bestimmtes Feld betrchtet wird, gehen wir in das Objekt dahinter und gucken uns dort die vorhandenen Felder an mittels for eahc Shcleife über die otherfields Felder.
Iterieren also über die verschiedenen Felder des (fremden) Objekts.
Auch da sollte die entsprechende Codezeile, ähnlich oben, doch eigentlich gerade verhindern dass schon zugewiesene Sachen wieder neu initialisiert werden?

Also ich checke derzeit meinen Fehler echt noch nicht :-/

@KonradN

Sehen tu ichs, aber viel zu viel Info die ich nicht durchsteige.
Vor Allem scheint mir das sowas wie eine "Programmierphilosophie" ohne dass ich mir so recht vorstellen kann wie das in real aussieht bzw. auf das aktuelle Programm angewendet.


Edit: Ich glaube, ich kapiere endlich das Problem am aktuellen Code.
Bleibt mir dann wohl doch nur eine strikte Topdownhierarchie übrig wenn ich es mit Reflection hinkriegen will.
 

KonradN

Super-Moderator
Mitarbeiter
Sehen tu ichs, aber viel zu viel Info die ich nicht durchsteige.
Vor Allem scheint mir das sowas wie eine "Programmierphilosophie" ohne dass ich mir so recht vorstellen kann wie das in real aussieht bzw. auf das aktuelle Programm angewendet
Würde es Dir helfen, da dann etwas mehr Code zu sehen, wie sowas bei Dir aussehen könnte?

Generell ist es immer schön, eine Rückmeldung bekommen. Egal, wie die aussieht. Kann also auch gerne etwas sein a.la. "Sorry, ich verstehe einfach nicht, was Du mir sagen willst" über etwas wie "Die Idee klingt nicht so sinnvoll für mich" bis hin zu konkreten Nachfragen. Die Idee ist ja, Dir zu helfen und da kannst Du gerne steuern, was Du für hilfreich oder sinnvoll hältst.
 

KonradN

Super-Moderator
Mitarbeiter
Ein ganz einfaches Beispiel, wie so eine ganz simple Dependency Injection Lösung aussehen könnte, findet sich jetzt unter:

Die Idee ist ganz einfach: Es gibt eine Klasse ApplicationContext. Die hält sozusagen im Kontext alle wichtigen Instanzen und hält diese aufrufbar.

Dabei gibt es eine Art Singleton: ApplicationContext ist zwar kein Singleton aber es wird ein default Context unterstützt, der halt über eine statische Methode getDefaultContext jederzeit aufrufbar ist und die man nutzen könnte.

Die eigentliche Methode ist dann getBean(Class) welche dann eine Instanz der übergebenen Klasse zurück gibt. Wenn schon für die Klasse eine Instanz da ist, dann wird diese übergeben.

Wenn nicht, wird der erste Constructor genommen um den dann aufzurufen. Für jeden Parameter wird dann rekursiv nach einer Instanz gesucht (und diese ggf. erstellt).

Um keine Endlosschleife zu bekommen wird in einem Set gemerkt, welche Klassen derzeit erstellt werden. Eine Ring-Abhängigkeit führt somit zu einer Exception.

Weiterhin habe ich ein Interface Initializable erstellt, welche implementiert werden kann. Alle Instanzen, die Initializable implementieren, werden nach der Erstellung der Instanzen noch zusätzlich initialisiert.

TestClassA und TestClassB zeigen, wie man das nutzen könnte.

Java:
package de.kneitzel.simpledi;

import java.lang.reflect.Constructor;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Simple Dependency Injection
 * Thread: https://www.java-forum.org/thema/reflection-gone-wrong.198603/
 */
public class ApplicationContext {

    /**
     * default application context.
     */
    private static ApplicationContext defaultContext;

    /**
     * Already created instances.
     */
    private Map<String, Object> beans = new ConcurrentHashMap<>();

    /**
     * List of classes that we currently build.
     */
    private Set<Class> currentlyBuilding = new HashSet<>();

    public ApplicationContext() {
        beans.put(getClass().getName(), this);
    }

    /**
     * Gets the default application context.
     * @return Default application context.
     */
    public static ApplicationContext getDefaultContext() {
        if (defaultContext == null) {
            synchronized (ApplicationContext.class) {
                if (defaultContext == null) {
                    defaultContext = new ApplicationContext();
                }
            }
        }
        return defaultContext;
    }

    public synchronized <T> T getBean(final Class<? extends T> clazz) {
        List<Initializable> toInitialize = new ArrayList<>();
        try {
            return getBean(clazz, toInitialize);
        } finally {
            toInitialize.forEach(Initializable::initialize);
        }
    }
    private synchronized <T> T getBean(final Class<? extends T> clazz, final List<Initializable> toInitialize) {
        if (currentlyBuilding.contains(clazz)) {
            throw new IllegalStateException("Circular dependency when building " + clazz.getName());
        }

        System.out.println("Creating instance for " + clazz.getSimpleName());

        T instance = null;
        try {
            instance = (T) beans.get(clazz.getName());
            if (instance != null) {
                return instance;
            }

            Constructor<T> constructor = (Constructor<T>) clazz.getConstructors()[0];
            Class[] parameterClasses = constructor.getParameterTypes();
            Object[] parameter = Arrays.stream(parameterClasses)
                    .map(c -> getBean(c, toInitialize))
                    .toArray();

            instance = constructor.newInstance(parameter);
            System.out.println("Added instance for " + clazz.getSimpleName() + " to beans (" + instance + ")");
            beans.put(clazz.getName(), instance);
            if (instance instanceof Initializable initializable) {
                toInitialize.add(initializable);
            }
        } catch (Exception ex) {
            currentlyBuilding.clear();
            throw new IllegalStateException("Unable to instantiate class " + clazz.getName(), ex);
        } finally {
            currentlyBuilding.remove(clazz);
        }

        return instance;
    }

}

Java:
package de.kneitzel.simpledi;

/**
 * Interface that describes an Object that can be initialized.
 */
public interface Initializable {
    /**
     * Initialize the instance.
     */
    void initialize();
}

Java:
package de.kneitzel.simpledi;

public class TestClassA {
    private TestClassB b;

    public TestClassA(TestClassB b) {
        this.b = b;
    }

    public TestClassB getB() {
        return b;
    }
}

Java:
package de.kneitzel.simpledi;

public class TestClassB implements Initializable {
    private ApplicationContext context;

    private TestClassA a;

    public TestClassB(ApplicationContext context) {
        this.context = context;
    }

    @Override
    public void initialize() {
        System.out.println("Initialize: Getting instance for TestClassA");
        a = context.getBean(TestClassA.class);
    }

    public ApplicationContext getContext() {
        return context;
    }

    public TestClassA getA() {
        return a;
    }
}
(Alternativ kann man auch auf den context als Parameter verzichten und ApplicationContext.getDefaultContext() verenden!)

Java:
package de.kneitzel.simpledi;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class ApplicationContextTest {
    @Test
    public void testApplicationContext() {
        ApplicationContext context = ApplicationContext.getDefaultContext();
        TestClassA a = context.getBean(TestClassA.class);
        assertNotNull(a);

        TestClassB b = a.getB();
        assertAll(
                () -> assertNotNull(b),
                () -> assertNotNull(b.getA()),
                () -> assertEquals(a, b.getA()),
                () -> assertNotNull(b.getContext()),
                () -> assertEquals(context, b.getContext())
        );
    }
}

Das einfach einmal als wirklich minimale Lösung. Hier werden jetzt noch keine Interfaces und Co unterstützt. Das wäre halt alles doch deutlich komplexer. Aber so ein Ansatz könnte für Dich schon gut sein. Deine Konstuktoren könnten alle Default Konstruktoren sein, die dann in initialize die anderen Elemente bekommt. Und da würde man dann den Startschuss über ein AppicationContext.getDefaultContext().getBean(GameMaster.class) oder so geben.

Ich hoffe, dass dies nun anschaulicher und einfacher zu verstehen war.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
B Java Reflection Probleme beim wehcselseitigen Referenzieren zweier Klassen/Objekte Allgemeine Java-Themen 14
J Reflection Aufruf: .class durch .dll ersetzen Allgemeine Java-Themen 4
T Reflection - Abprüfen von Klasseneigenschaften Allgemeine Java-Themen 3
perlenfischer1984 Reflection : Element in generische Liste hinzufügen Allgemeine Java-Themen 4
J Reflection-Method Hook? Allgemeine Java-Themen 14
J Reflection mit Klasse und Subklasse Allgemeine Java-Themen 11
X public Getter ersetzen (Reflection?!) Allgemeine Java-Themen 3
A Reflection Problem Allgemeine Java-Themen 15
K Generic class und Reflection Allgemeine Java-Themen 2
G SecurityManager/Policy-File/Reflection Allgemeine Java-Themen 0
N Per Reflection auf (method) local inner class zugreifen? Allgemeine Java-Themen 3
E Reflection? Frage Allgemeine Java-Themen 4
B Reflection, invoke dynamisch befüllen Allgemeine Java-Themen 3
T Reflection Allgemeine Java-Themen 11
G Klassen Cast auf Argumentenklasse via Reflection? Allgemeine Java-Themen 10
H Collections Generics und Reflection Allgemeine Java-Themen 6
TheWhiteShadow Reflection-Lösung für Objektkopien Allgemeine Java-Themen 3
B Reflection: getMethods schlägt fehl (NoClassDefFoundE)) Allgemeine Java-Themen 15
P Reflection "Cast" Allgemeine Java-Themen 5
D Reflection-Performance Allgemeine Java-Themen 7
S Reflection und Generic Allgemeine Java-Themen 9
W Reflection -> getMethod bei optionalen Parametern Allgemeine Java-Themen 4
Kr0e Reflection - Instanz einer Klasse erstellen, ohne Default-Constructor Allgemeine Java-Themen 15
C Probleme mit dem Zugriff auf private Methode per reflection Allgemeine Java-Themen 2
C Zugriff auf private Methode per reflection geht nicht mehr Allgemeine Java-Themen 3
M Probleme mit Reflection Allgemeine Java-Themen 10
A Reflection Konstruktor Parameter Supertyp Allgemeine Java-Themen 2
D Vererbung, Reflection und automatischer Methodenaufruf Allgemeine Java-Themen 24
G Java Reflection Allgemeine Java-Themen 11
N Reflection: NoSuchMethodException obwohl Methode existiert Allgemeine Java-Themen 2
brunothg Problem mit Reflection Allgemeine Java-Themen 6
M reflection in inner classes Allgemeine Java-Themen 7
T Class-files zur Laufzeit zu Reflection-Zwecken laden Allgemeine Java-Themen 18
V Reflection - Field-Instanz und Annotations Allgemeine Java-Themen 6
Y Reflection und "extends" Schlüsselwort Allgemeine Java-Themen 3
R Reflection Problem Allgemeine Java-Themen 4
N getName() of reflection Object Allgemeine Java-Themen 4
P Reflection - Wie rufe ich die Felder einer Klasse in einer Methode der Basisklasse? Allgemeine Java-Themen 4
M Reflection Queue auslesen Allgemeine Java-Themen 6
N generic reflection Allgemeine Java-Themen 5
megachucky Java Reflection -> versteckte Felder finden? Allgemeine Java-Themen 3
MQue Reflection Exception Allgemeine Java-Themen 11
R Problem mit Reflection und Generics Allgemeine Java-Themen 3
ruutaiokwu Per Reflection membervariablen überschreiben / zuweisen / nullen Allgemeine Java-Themen 5
T Zugriff per Reflection o.ä. möglich? Allgemeine Java-Themen 18
R Reflection: Membervariablen-Initialwerte Allgemeine Java-Themen 4
G Reflection Allgemeine Java-Themen 6
V reflection in java 5 und 6 Allgemeine Java-Themen 2
C Reflection Übergabeparamter überprüfen? Allgemeine Java-Themen 3
Ark Konstruktor per Reflection nicht bekannt Allgemeine Java-Themen 4
Q Objekte durch Reflection erzeugen Allgemeine Java-Themen 18
G [Reflection + WebService] Methodenaufruf an einem Proxy Allgemeine Java-Themen 11
T Parameternamen mit Reflection-API Allgemeine Java-Themen 14
M reflection mit parametern Allgemeine Java-Themen 8
M Reflection Frage Allgemeine Java-Themen 7
G Code vereinfachen bei Reflection Allgemeine Java-Themen 4
I reflection get inner static classes Allgemeine Java-Themen 2
C Reflection Allgemeine Java-Themen 12
Y Reflection - wie kann ich eine Methode benutzen Allgemeine Java-Themen 4
Y Reflection - wie anwenden/benutzen bei folgendem Problem Allgemeine Java-Themen 3
P Java Reflection API - ResultDataModel ? Allgemeine Java-Themen 3
R Object Dynamisch erzeugen (Reflection API) Allgemeine Java-Themen 22
N Design-Frage: persistent machen per Reflection Allgemeine Java-Themen 3
G Reflection: aktuelle bzw umschliessende Methode? Allgemeine Java-Themen 7
G per Reflection auf private Klassenattribute zugreifen? Allgemeine Java-Themen 9
G Reflection objekt mit generics erzeugen Allgemeine Java-Themen 5
M per reflection private attributsnamen auslesen Allgemeine Java-Themen 3
G Interface - Klassen implementieren das - Reflection ok? Allgemeine Java-Themen 4
C Reflection Problem! Allgemeine Java-Themen 2
G Object cast via Reflection Allgemeine Java-Themen 8
T Reflection und Typsicherheit Allgemeine Java-Themen 5
Q Umgang mit Reflection Allgemeine Java-Themen 4
S reflection Allgemeine Java-Themen 12
X Reflection performant? Allgemeine Java-Themen 11
M Reflection und Serializable Allgemeine Java-Themen 5
A Reflection - Variablen innerhalb einer Methode ermitteln Allgemeine Java-Themen 9
F Performance von Reflection vs Statisches Coden Allgemeine Java-Themen 4
the[V]oid Primitive Arrays per Reflection erzeugen? Allgemeine Java-Themen 2
W Package via Reflection parsen Allgemeine Java-Themen 4
R Interface mittels Reflection implementieren Allgemeine Java-Themen 8
M reflection Integer != int ? Allgemeine Java-Themen 3
M reflection invoce Allgemeine Java-Themen 3
K Reflection:Zugriff auf innere Klassen Allgemeine Java-Themen 4
N NoSuchMethodException bei Reflection-Versuch, aber warum? Allgemeine Java-Themen 3
byte Methodenaufruf per Reflection? Allgemeine Java-Themen 2
M Generics und Reflection Allgemeine Java-Themen 3
M Array per Reflection manipulieren Allgemeine Java-Themen 5
M Klassen von ausserhalb des Jars per Reflection nachladen Allgemeine Java-Themen 2
thE_29 Reflection Allgemeine Java-Themen 6
M statische Methode per reflection aufrufen Allgemeine Java-Themen 2
B Frage zu Reflection Allgemeine Java-Themen 3
V Reflection - Problem Allgemeine Java-Themen 3
clemson Reflection Interface Allgemeine Java-Themen 4
G mit reflection an die felder einer klasse rankommen Allgemeine Java-Themen 4
N Reflection: An Package einer .class-Datei kommen Allgemeine Java-Themen 2
N Reflection: Feststellen ob eine Klasse abstract ist Allgemeine Java-Themen 3
B Reflection Allgemeine Java-Themen 10
C Reflection und variable casten ! Allgemeine Java-Themen 10
C Reflection - Kurze Beschreibung? Allgemeine Java-Themen 3
F Frage zum Thema Reflection Allgemeine Java-Themen 13

Ähnliche Java Themen


Oben