[Java] Objekt 'dynamisch' erzeugen

Hallo!

Ich würde gerne eine Objekt über den Namen den ich als String habe erzeugen. Sinn und Zweck der Sache wäre es, dass ich einen Haufen Klassen haben kann die ein bestimmtes Interface implementieren und ich über ein File o. ä. konfigurieren kann _welche_ Klasse er nimmt. Irgendwie so:

ITest oTest = (ITest)getObject( „package.class“ );

Das getObject() suche ich jetzt halt. Kennt jemand etwas in die Richtung? Sollte einfach ein Object zurückgeben, dass ich dann dementsprechend downcasten kann.

Danke und schöne Grüße, Robert

Hallo Robert!

Ich würde gerne eine Objekt über den Namen den ich als String
habe erzeugen. Sinn und Zweck der Sache wäre es, dass ich
einen Haufen Klassen haben kann die ein bestimmtes Interface
implementieren und ich über ein File o. ä. konfigurieren kann
_welche_ Klasse er nimmt.

 try
 {
 Class c = Class.forName("Beispielklasse");
 meinObjekt = c.newInstance();
 }
 catch(ClassNotFoundException e)
 {
 System.out.println("Klasse nicht gefunden");
 }
 catch(java.lang.IllegalAccessException ie)
 {
 ie.printStackTrace(System.err);
 }
 catch(java.lang.InstantiationException insterr)
 {
 insterr.printStackTrace(System.err);
 }

So kannst Du ein neues Objekt generieren, indem Du der Methode forName(), den Namen der gewünschten Klasse übergibst.

Grüße, Tanja

Danke! :smile:

Grüße, Robert

Hallo Robert!

Danke! :smile:

Ich habe im Moment das gleiche Problem wie Du, funktioniert es bei Dir inzwischen?

Ich möchte das Strategy Pattern (-> Gamma) für eine Raum-Kurs Allokation implementieren. Es müssen also Kurse auf bestimmte Räume verteilt werden. Dabei können verschiedene Strategien eingesetzt werden. Dies habe ich als Interface realisiert. Die ausgewählte Strategie wird dem Kurs-Konstruktor gleich als String mitgegeben.

Meine Allocation-Klasse soll nun je nach gewählter Strategie das jeweilige Strategiy-Objekt erzeugen. Bisher habe ich das zum Testen einfach mal so gemacht…

 switch (strategyNo)
 {
 case 1:
 new Strategy1(kursID);
 break;

 case 2:
 new Strategy2(kursID);
 break;

 // usw...
 } 

…aber das ist natürlich im Hinblick auf Erweiterabarkeit nicht so schön. Es sollten eigentlich dynamisch neue Strategien hinzugeladen werden können, indem man einfach neue Strategy-Implementierungen bereitstellt, die vom Strategy-Interface abgeleitet sind.

Ich würde jetzt also gerne die Strategy-Objekte auch dynamisch mit Class.forName() erzeugen, aber irgendwie bekomme ich das nicht hin. Der Compiler sagt immer: „Inkompatible Typen: java.lang.Object wurde gefunden, gui.Strategy ist erforderlich.“ Casten bringt auch nichts.

Es funktioniert jedoch, wenn ich…

 Class c = Class.forName(strategy);
 Object myStrategy = c.newInstance(); 

… schreibe, aber so kann ich dem Strategy-Konstruktor die Variable kursID nicht mitgeben. Wie mache ich es richtig?

Grüße, Tanja

Ich habe im Moment das gleiche Problem wie Du, funktioniert es
bei Dir inzwischen?

Job, hab mich inzwischen ein bißchen damit auseinandergesetzt. :smile:

… schreibe, aber so kann ich dem Strategy-Konstruktor die
Variable kursID nicht mitgeben. Wie mache ich es richtig?

Also, du kannst dir über das Class-Object das du erzeugst mit der Methode getConstructors() ein Array mit Elementen der Klasse Constructor holen. Dort kannst du dir einen Constructor raussuchen und mit newInstance() damit ein Objekt erzeugen. Die Parameter übergibst du da dem newInstance als Array of Objects.

Ich hab allerdings einen anderen Weg gewählt, ich habe mehrere Methoden zur Verfügung gestellt um die notwendigen Daten zu übermitteln und rufe dann eine Methode initialize() auf, den Constructor verwende ich gar nicht.

Schaut in etwa so aus:

Class cTemp = Class.forName( sClassName );
IInterface oObject = (IInterface)cTemp.newInstance();
oObject setXYZ( XYZ );
oObject setABC( ABC );
oObject.initialize();

Grüße, Robert

Hallo Robert!

Methoden zur Verfügung gestellt um die notwendigen Daten zu
übermitteln und rufe dann eine Methode initialize() auf, den
Constructor verwende ich gar nicht.

Ich finde diese Idee sehr gut und habe es deshalb so…

 Class c = Class.forName(strategy);
 myStrategy = (Strategy)c.newInstance();
 myStrategy.initialize(kursID);

…versucht, wobei die initialize()-Methode die Funktion Konstruktors übernimmt. Wenn ich das Programm so starte, bekomme ich aber jedes Mal die Fehlermeldung „java.lang.IllegalAccessException Strategy1“, obwohl es wunderbar funktioniert hat, als ich es noch mit dem switch-Konstrukt gemacht habe. :frowning: Wie kommt das?

Laut Java-Referenz tritt eine IllegalAccessException auf, wenn „the currently executing method does not have access to the definition of the specified class, because the class is not public and in another package. An instance of this class can also be thrown when an application tries to create an instance of a class using the newInstance method in class Class, but the current method does not have access to the appropriate zero-argument constructor.“ Dies ist bei mir aber nicht der Fall.

Also, du kannst dir über das Class-Object das du erzeugst mit
der Methode getConstructors() ein Array mit Elementen der
Klasse Constructor holen. Dort kannst du dir einen Constructor
raussuchen und mit newInstance() damit ein Objekt erzeugen.
Die Parameter übergibst du da dem newInstance als Array of
Objects.

Ich habe auch diese Möglichkeit ausprobiert, aber irgendwie war ich auch nicht so erfolgreich. Hast Du davon evtl. auch Beispielcode? Ich würde aber sowieso das obere präferieren, da es viel einfacher ist und ich der Strategy-Klasse sowieso nur eine einzige Variable übergeben muß.

Grüße, Tanja

Wenn ich das Programm so starte,
bekomme ich aber jedes Mal die Fehlermeldung
„java.lang.IllegalAccessException Strategy1“
[…]
Dies ist bei mir aber nicht der
Fall.

Hmmm, sorry, bei mir gehts ohne Probleme. Vielleicht ist das Interface oder die Klasse auf die du castest nicht public?

Ich habe auch diese Möglichkeit ausprobiert, aber irgendwie
war ich auch nicht so erfolgreich. Hast Du davon evtl. auch
Beispielcode?

Hmmm, meine Hauptquelle (MSDN) hat leider kaum Beispiele, aber ich hab schnell selber eines zusammengeschrieben, funkt auch. Allerdings habe ich nicht den vorher beschrieben Weg über das Array geholt, man kann sich vom Class-Objekt auch direkt ein bestimmtes Constructor-Objekt holen, sieh selbst:

// In diesem Array of Class sind die Parametertypen
// des Constructors den ich suche gespeichert
Class[] cParam = new Class[1];
cParam[0] = Class.forName( "java.lang.String" );

// Um diese Klasse geht es
Class cTemp = Class.forName( "java.lang.String" );

// Der Constructor der die vorher in cParam gespeicherten
// Parametertypen entgegennimmt wird geholt (Copy-Constructor)
Constructor oConstr = cTemp.getConstructor( cParam );

// In diesem Array sind die Objects die übergeben werden
// sollen gespeichert
Object[] oParam = new Object[1];
oParam[0] = new String( "TESTSTRING" );

// Jetzt instanziere ich über den Constructor ein
// neues Objekt
String sTest = (String)oConstr.newInstance( oParam );

// Und siehe da, es hat funktioniert. :smile:
System.out.println( sTest );

Ich hoffe das hilft dir weiter … :o)

Grüße, Robert

Hallo Robert!

Hmmm, sorry, bei mir gehts ohne Probleme. Vielleicht ist das
Interface oder die Klasse auf die du castest nicht public?

Doch. Obwohl ich alle notwendigen Klassen public deklariert habe, hat mir der Compiler weiterhin die ganze Zeit lustige
IllegalAccessExceptions ausgespuckt!

Die Lösung meines Problems war nun, daß man bei Class.forName() unbedingt das Package angeben muß, in dem die jeweilige Klasse liegt, also z. B.

 Class c = Class.forName("gui." +strategy);

sonst gibt es eine IllegalAccessException. Eigentlich
logisch, aber ich war mal wieder zu dusselig, um gleich auf diese glorreiche Idee zu kommen :wink:

Hmmm, meine Hauptquelle (MSDN) hat leider kaum Beispiele, aber
ich hab schnell selber eines zusammengeschrieben

Danke auch für den Beispielcode.

Grüße, Tanja

Die Lösung meines Problems war nun, daß man bei
Class.forName() unbedingt das Package angeben muß, in dem die
jeweilige Klasse liegt, also z. B.

Seltsam nur, dass er eine IllegalAccessException wirft, und keine ClassNotFoundExeception, was eigentlich das logischere wäre.

Aber wahrscheinlich wäre das schlicht zu einfach. :smile:

Grüße, Robert