PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : java: klassenname aus string lesen oder was?


AHF
2003-09-30, 16:58:33
grüße bürger,

ich möchte in java eine neue instanz einer nicht deklarierten bekannten externen klasse erstellen.

normalerweise geht es ja so:

private <klasse> k;
k = new <klasse> ();

stelle mir das ungefähr so vor:

die aufrufende klasse (main) liegt im selben verzeichnis wie die zu erzeugende, so dass sie darauf zugreifen kann. den klassenname will ich über den filechooser bekommen.

sähe dann so etwa aus:

JFileChooser chooser = new JFileChooser();
String FilePath = chooser.getSelectedFile().getName();

<Klassentyp aus FilePath> k = new <Klassentyp aus FilePath> ();

ich hoffe, jemand versteht, was ich meine und kann helfen. danke.

HellHorse
2003-09-30, 17:32:26
Du erzeugst zuerst mit Class.forName(className) ein Class Objekt. Darauf führst du dann .getConstructors() aus. Du suchst dir einen passenden aus und führst darauf newInstance() mit passendem Paramter aus.

Hoffe das hilft

Hier noch die passenden Links
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Class.html
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Constructor.html

Abe Ghiran
2003-09-30, 19:25:52
Hi...
Noch als Ergänzung zu dem was HellHorse geschrieben hat: Mit getInstance() kriegst du natürlich erst mal ganz allgemein ein Object.

Du könntest jetzt zwar über das Reflection api abfragen, welche Methoden dieses Objekt, bzw. die deklarierende Klasse, anbietet was aber natürlich sehr umständlich ist.

In allen Fällen wo ich bisher diesen Mechanismus benutzt habe, haben die Klassen die man da zur Laufzeit läd und instanziiert ja schon irgendwas gemeinsam (bei plugins für eine applikation z.b. init(), start(), stop(), doSomething()).
Dann macht es natürlich Sinn, das in einem Interface zu verpacken und dann bei der dynamisch geladenen Klasse zu überprüfen, ob sie dieses Interface implementiert. Dann kannst du das mit getInstance erzeugte Objekt auch gecastet einer passenden Variable zuordnen.

Denn das was du hier geschrieben hast

<Klassentyp aus FilePath> k = new <Klassentyp aus FilePath> ();

geht so nicht, du kannst den Typ für die Variable k ja nicht zur Laufzeit festlegen.

Ich würde dann sowas vorschlagen:

// das class object für das interface laden, das brauchen wir später
Class interfaceClass = Class.forName("ApplicationPlugin");

JFileChooser chooser = new JFileChooser();
String FilePath = chooser.getSelectedFile().getName();

Class class = Class.forName(FilePath);

// testen ob die geladene Klasse das Interface implementiert
if(!interfaceClass.isAssignableFrom(class))
throw new Exception(class.getName()+" is not a valid plugin.");

ApplicationPlugin p = (ApplicationPlugin)class.getInstance();

p.init();
p.start();


Wobei ApplicationPlugin das Interface ist, das die Methoden festlegt, die eine dynamisch geladenen Klasse implementieren muß.

Man muß das nicht so machen aber es macht das Leben leichter und in vielen Fällen bietet sich das eh an. Mir ist persönlich nur eine Anwendung bekannt, wo man wirklich völlig ohne Vorwissen zur Laufzeit mit irgendwelchen vorher unbekannten Klassen hantieren musste und genauer war das auch nur ein Framework, damit man sich später als Programmierer einer Applikation eben nicht mehr damit rumschlagen musste.

Grüße, Jan

AHF
2003-09-30, 22:56:31
hallo,

danke für eure antworten.

geht so nicht, du kannst den Typ für die Variable k ja nicht zur Laufzeit festlegen.

wenn das so ist geht das wohl leider gar nicht so wie ich wollte.
angenommen, ich wähle mit dem filechooser die datei myclass.class aus, die eine klasse enthält, von der ich weiß, dass mein main damit umgehen kann. von dieser klasse möchte ich eine instanz erzeugen, ohne dass der klassentyp vorher irgendwo in main deklariert ist.

ich kann zwar einem neutralen object eine instanz der klasse zuweisen, allerdings gehen dann die darin implementierten methoden nicht.

momentan siehts so aus:

JFileChooser chooser = new JFileChooser();
String FilePath = chooser.getSelectedFile().getName();
Class Class = null;
Object o;

...

Class = Class.forName(FilePath);
o = Class.newInstance();

o.setBla() // funktioniert nicht

...und casten oder parsen will ich nicht, da ich ja sonst alle klassentypen zumindest deklariert haben müsste, was sehr unflexibel wäre. bei jeder neu geschriebenen klasse müsste ich ja dann das hauptprogramm ändern

HellHorse
2003-10-01, 00:02:09
Original geschrieben von Awesome
...und casten oder parsen will ich nicht, da ich ja sonst alle klassentypen zumindest deklariert haben müsste, was sehr unflexibel wäre. bei jeder neu geschriebenen klasse müsste ich ja dann das hauptprogramm ändern
Schon mal was von Polymorphismus gehört ;)
Falls du bei allen Objekten sowieso nur blah() ausführen willst, machst du am besten ein Interface Blahable

public interface Blahable {
public void blah();
}


JFileChooser chooser = new JFileChooser();
String FilePath = chooser.getSelectedFile().getName();
Class Class = null;
Blahable o;

...

Class = Class.forName(FilePath);
//vielleicht noch prüfen, ob Blahable implementiert wird
o = (Blahable) Class.newInstance();

o.bla() // funktioniert

Du kannst ja der Refelections API überprüfen, ob die Klasse Blahable implementiert. Falls nicht, kannst du ja nix machen.

Eine andere Möglichkeit ist, du machst eine GUI und überlässt es dem User, die Methoden auszuwählen, die ausgeführt werden sollen.

Es würde helfen, wenn wir wüssten, was du damit machen willst.

AHF
2003-10-01, 00:40:53
Original geschrieben von HellHorse
Schon mal was von Polymorphismus gehört ;)


öhm...vor äonen. weiss nicht mehr, was das ist. aber ich bin ja auch noch ein java-novize.

interface...war das nicht so eine sammlung abstrakter methoden?

Es würde helfen, wenn wir wüssten, was du damit machen willst.

verschiedene geometrien in ein universum laden, die methoden der geometrieklassen sind überall dieselben. der user soll über eine gui und filechooser die entprechenden klassen laden können.

...dabei fällt mir ein weiteres problem ein, kann ich beim filechooser die extensions verbergen bzw. gibts was fertiges, womit ich strings von hinten um die extensions beschnippeln kann, oder muss ich mir was basteln?

HellHorse
2003-10-01, 17:04:18
Ein Interface ist eine Art Protokoll. Es schreib Methoden vor, die eine Klasse anbieten muss, wenn sie es implementieren will. Der Vorteil gegenüber einer abstrakten Methode ist, dass eine Klasse (in Java) bloss von einer abstrakten Klasse erben, aber beliebig viele Interfaces implementieren kann. Zudem befreit man sich von der Klassenhierarchie, was zu zusätzlicher Flexibilität führt.

Der von Abe vorgeschlagene Weg, scheint mir der richtige zu sein.
Du packst einfach die Methoden, die du aufrufen willst in ein Interface.

public interface Geometry {
public void init();
public void start();
public TollesObjekt tolleMethode(TollerParamter tollerParamter);
}

Der Punkt kommt jetzt, du sprichtst die Objekte bloss über das Interface an. Schlussendlich kann es dir ja egal sein, von welcher Klasse es ist oder wovon die erbt, du brauchst ja nur die Methoden, die im Interace stehen. Du kannst so beliebige Objekte ansprechen, solange ihre Klassen das Interface implementieren.

Class = Class.forName(FilePath);
Geometry geometry = (Geometry) Class.newInstance();
...
geometry.init();
geometry.start();
...

AHF
2003-10-02, 13:13:04
HellHorse,

genau das hat gebracht, was ich wollte. vielen dank nochmals, auch an Abe Ghiran.

HellHorse
2003-10-02, 21:07:21
NP, feut mich immer, wenn ich helfen kann. :)