Objekt-Array mit dynamischer Zuweisung in Java?

Wie kann man zur Laufzeit ein Objekt erzeugen, das unterschiedliche Typen beinhalten kann?

Meine Klasse sieht ungefähr so aus:

public Object Obj[] = new Object[]{new Subklasse1(Wert), new Subklasse2(Wert)} // Die „richtige“ Initialisierung erfolgt erst später…

class cHauptKlasse {
}
class Subklasse1 extends cHauptKlasse {
Subklasse1(int Wert); // Constructor
public void Methode1() {}
}
class Subklasse2 extends cHauptKlasse {
Subklasse2(int Wert); // Constructor
public void Methode1() {}
}

In einer Auswahl soll aus Subklasse1, Subklasse2, Subklasse3, … ausgewählt werden (Anzahl beliebig, von jedem aber maximal einer), für jeden Eintrag soll ein Objekt in einem Array abgelegt werden, zur Designzeit ist aber nicht bekannt, was ausgewählt wird, nur was alles ausgewählt werden kann.
Nun soll für jedes Element in diesem Objekt-Array eine Methode ausgeführt werden.

Obj[0] = new Subklasse1(Wert);
Obj[1] = new Subklasse2(Wert);

beliebigeAktion(Obj[0].Methode1);

Hier weis ich nicht weiter, weil Methode1 ja gar nicht bekannt sind.

Vermutlich geht das mit dem Reflection-Modell, ich weis aber nicht wie man das in diesem Fall anwenden kann.
Vielen Danke für jegliche Hilfe!
Gruß
Marc

Hallo Marc,
Definiere ein Interface, das Methode1() deklariert und das all deine Subklassen implementieren. Dann kannst du alle deine Objekte in das Array stecken, und wenn du eine Methode aufrufen willst, dann machst du einfach vorher einen TypeCast zum Typ des Interfaces, dann sollte automatisch die jeweilige Methode der Subklasse aufgerufen werden.

public Interface MyInterface() {
public void Methode1();
}

((MyInterface)Obj[0]).Methode1();

Ich bin nicht ganz sicher, ob du so aus einem Array Casten kannst, falls nicht, nimm einen Vector, damit gehts sicher.
Gruss
M.

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

((MyInterface)Obj[0]).Methode1();

Ich bin nicht ganz sicher, ob du so aus einem Array Casten
kannst, falls nicht, nimm einen Vector, damit gehts sicher.

Das würde schon gehen. Du kannst den Code aber recht einfach typsicher machen, indem du das Array gleich für das Interface definierst:

MyInterface Obj[] = new MyInterface[] { };

Obj[0].methode1();

Grüße, Robert

Moin

Wie kann man zur Laufzeit ein Objekt erzeugen, das
unterschiedliche Typen beinhalten kann?

schonmal über einen Proxy nachgedacht… ?

public Object Obj[] = new Object[]{new Subklasse1(Wert), new
Subklasse2(Wert)}

das ist OK.

class cHauptKlasse {
}
class Subklasse1 extends cHauptKlasse {
Subklasse1(int Wert); // Constructor
public void Methode1() {}
}
class Subklasse2 extends cHauptKlasse {
Subklasse2(int Wert); // Constructor
public void Methode1() {}
}

das nicht, so macht man das nicht… Was soll das Ding später können ?

Hier weis ich nicht weiter, weil Methode1 ja gar nicht bekannt
sind.

Doch, kann man über Reflection zur Laufzeit machen… Aber was hast du vor ?

cu

schonmal über einen Proxy nachgedacht… ?

Proxy??

Doch, kann man über Reflection zur Laufzeit machen… Aber was
hast du vor ?

Ok, jetzt beschreib ich es noch ein bisschen tiefer:

Es handelt sich hier um eine Implementierung von Threads, durch die Auswahl sollen mehrere gestartet werden (es soll eine Interaktion [pause/fortsetzen/…] und Auslesen der Werte stattfinden, also geht ein normaler Aufruf nicht)
Die Threads sind zur Visualierung von Sortieralgorithmen da, dem Benutzer soll eine parallele Ausführung dieser angezeigt werden. Diese sollen aber nicht nur selbständig ablaufen, sondern auch schrittweise vom Benutzer gesteuert werden.
Im Prinzip will ich das Objektarray durchgehen, und jedem Thread sagen, er möge bis zum nächsten Haltepunkt weiterzumachen, dann können die Werte aus dem Objektarray ausgelesen werden und irgendwie dargestellt werden.

Die Oberklasse enthält Methoden und Variablen, die jede Subklasse verwendet; der Einfachheit halber könnte man zwar auf die Oberklasse verzichten, aufgrund der besseren Wartung sollte diese vorhanden sein.
Der Methodenname in jeder SubKlasse ist der selbe: run(), Implementierung und Variablen dagegen unterschiedlich.

Vorhanden sind jetzt folgende Klassen/Interfaces

public interface MyInterface {
public void run();
}

public class MyClass extends Thread implements MyInterface {
public int var1[];
public int var2;
public MyClass(); //Konstruktor, leer, da er nie aufgerufen wird
public MyMethodX();
public MyMethodY();
}

public class SubKlasse1 extends MyClass {
public int varA;
public int varB;
public SubKlasse1(Array) { //Konstruktor
var1=Array;
var2=MyMethodX(irgendwas, varA);

}
public run() {
//der eigentliche Code für jede Klasse
}
}

public class SubKlasse2 extends MyClass {
public int varC;
public int varD;
public SubKlasse2(Array) { //Konstruktor
var1=Array;
var2=MyMethodX(irgendwas, varC);

}
public run() {
//der eigentliche Code für jede Klasse
}
}

im Hauptprogramm:
public Object Obj[] = new Object[]{};

Code:
Obj = new Object[]{new SubKlasse1(Array), new SubKlasse2(Array)}; //das gleiche Array
Obj[0] = new SubKlasse1(Array);
Obj[1] = new SubKlasse2(Array);
(MyInterface) Obj[0].start(); //start() ist unbekannt, run() durch den Typcast dagegen schon

Jetzt kann man zwar auf die Funktionen, die in MyInterface definiert wurden, zugreifen, aber auf die Variablen nicht - im Interface können ja nur Konstanten abgelegt werden.
Wie bekommt man das trotzdem zum Laufen?

Eine Alternative zum Interface wäre doch diese Möglichkeit, oder?
((SubKlasse1) Obj[0]).start();
Nur, wie könnte man hier umgehen, dass mit „instanceof“ der Objekttyp festgestellt werden muss und dann für die entsprechende Klasse die Methode/Variable aufgerufen/abgefragt werden kann (dazu können dann auch in den SubKlassen auch identische Variablen verwendet werden)
Kann man irgendetwas machen, z.B. Typcast auf MyClass und die Methoden der Subklasse verwendet werden (wenn diese beispielsweise überschrieben wurden)?

Gruß
Marc

Hallo Marc,

Die Threads sind zur Visualierung von Sortieralgorithmen da,
dem Benutzer soll eine parallele Ausführung dieser angezeigt
werden. Diese sollen aber nicht nur selbständig ablaufen,
sondern auch schrittweise vom Benutzer gesteuert werden.

Hui klingt genau gleich wie was ich auch schonmal machen musste :smile: Mein Ansatz war irgendwie anders, aber anyways…

Ich würde in so nem Array den größten gemeinsamen Nenner nehmen, alle Objekte da drin sind ja offenbar abgeleitet von MyClass, also wieso nicht so:

MyClass[] obj = new MyClass[] { new SubKlasse1(Array), new SubKlasse2(Array) };
obj[0].start();

Wenn du doch ein Object[] nehmen willst, dann solltest du nach MyClass oder nach Thread casten (was ja die Methode start() bietet), würde beides funktionieren. Ein Interface brauchst du dafür nicht, das Interface in deinem Beispiel bewirkt übrigens genau nichts, das Interface Runnable sieht genauso aus wie deins und wird von Thread implementiert.

Kann man irgendetwas machen, z.B. Typcast auf MyClass und die
Methoden der Subklasse verwendet werden (wenn diese
beispielsweise überschrieben wurden)?

Das passiert eh automatisch…

Grüße
Bruno

schonmal über einen Proxy nachgedacht… ?

Proxy??

java.lang.reflect.Proxy, für die Zwecke braucht man das aber nicht.

Die Threads sind zur Visualierung von Sortieralgorithmen da, dem Benutzer soll eine parallele Ausführung :dieser angezeigt werden. Diese sollen aber nicht nur selbständig ablaufen, sondern auch schrittweise vom :Benutzer gesteuert werden.

Also 2 Varinaten: einmal stop&Go und einmal „durchlaufen bis zum ende“

Im Prinzip will ich das Objektarray durchgehen, und jedem Thread sagen, er möge bis zum nächsten Haltepunkt :weiterzumachen, dann können die Werte aus dem Objektarray ausgelesen werden und irgendwie dargestellt werden.

Ohne dir auf die Füsse tretten zu wollen: du hast nocht nicht viel mit Threads gearbeitet, oder ? Der AWT-thread sagt dir auch nix ? Es ist eine Hausaufgabe ? 3x ja ?

OK, ich hol jetzt weiter etwas aus und beschreibt eine komplexere und „korrektere“ Lösung,… da es eine Hausaufgabe ist werd ich dir aber keine Komplett-lösung hinschreiben.

Du willst Threads starten die was berrechen und später die Werte zusammensuchen und darstellen. Die Quiz-Frage ist: welcher Thread sammelt die Werte ein und wie weiss er wann die anderen fertig sind ?

Das mit dem „auf’s-fertg-werden-warten“ ist einfach, dafür gibts Object.wait() (und notifyAll()). ABER:

Wenn du das in der Actionlistener-Methode (die Methode die aufgerufen wird wenn der User auf einen Button klickt oder sonst irgendwie mit der GUI arbeitet) machst wird es der AWT-thread sein. Während der AWT-thread in wait() auf die anderen wartet funktioniert die GUI gar nicht. Sie wird auf nix reagieren und nix anzeigen. Der User kann also 1. nicht sehen dass die Sortierung schon läuft und 2. sie auch nicht unterbrechen.

Also sollte man das NICHT im AWT-thread machen. Das geht anders besser:

Du baust dir ein zentrales Objekt DATA das alle Werte bekommt. In dem Object geben die Sortier-threads ihre Werte ab und kucken ob sie weitermachen sollen oder nicht. (Polling)

So in der Art

public synchronized boolean lastValues (int[] aktuelles_array, int SortiererID)

(Das int[]-array sollte eine Kopie sein oder kopiert werden. Nicht einfachso im Rechenthread und in DATA das gleiche Array verwenden)

Wenn das false zurückgibt soll sich der Thread erstmal schlafen legen, und zwar in DATA.wait(). Klickt der User auf „noch einen Schritt rechnen“ (oder "ganz-zuende-rechenen) rufts du DATA.notifyAll() auf. Dadurch laufen die sortier-threads wieder weiter. Wenn das Prog eh von anfang an durchlaufen soll kann die Methode auch immer true zurückgeben. Bei true sollen die Threads nicht warten und nicht „wait()“ aufrufen.

Damit wären die Daten schonmal threadsafe eingesammelt und die GUI funktioniert auch noch.

Bleibt noch das anzeigen:
Jetzt kommt wieder ein dummes Problem mit dem AWT-Thread: man kann in lastValues sich einfachso einen graphic-kontext (graphics2D-Object) besorgen und die Darstellung aktualiserien ABER:

Das führt fast immer zu Darstellungsfehlern. Deshalb gibt es „repaint()“ und „invokeLater(Runnable R)“. Für deine Zwecke (und eine gute Note) reicht „repaint(10)“. Du lässt DATA von JComponent erben und überschreibst (überlädest… wie sagt man das im Deutschen ?) „public synchronized void paint(Graphics g)“. (Das synchronized ist wichtig. Ohne wirst du diverse Multi-thread-Probleme aus dem 1-3 Semster in der Praxis kennenlernen) In der Methode malst du auf Basis der aktuellen Werte die Diagramme neu. paint darf nur im AWT-Thread ausgeführt werden… den man ja nicht aufhalten soll. Also ruft man ihn in lastValues mit „DATA.repaint(10)“. Daraufhin versucht der AWT-Thread möglichst bald „DATA.paint(Graphics g)“ aufzurufen.

Damit werden die Daten auch einigermassen schnell angezeigt. Es geht noch schneller, aber dann muss man gut auf Swing-Komponenten aufpassen.

Was das Starten der Threads angeht:

… User hat festgelegt welche Methoden er sehen will …
Runnable[] R = new Runnable [Anzahl_der_sortierer];

for (…){
bestimme zuständige Klasse/subklasse
initalisiere sie
trage sie in R[i] ein.
}
for (…){
R[i].start();
}

Wieso mit 2 Schleifen: „initalisiere sie“ wird etwas länger dauern als start(), die ersten Threads hätten somit einen erheblichen Zeitvorsprung bekommen. Deshalb 2 Schleifen.

Wieso start() und nicht run() ?
Neue Threads werden mit start() gestartet. run() ist nur eine normale Methode und würde für ein bisschen Verwirrung sorgen. (der AWT-Thread würde den ersten Sortierer bis zum Ende durchrechnen, dann den zweiten, dann den dritten… usw)

(R[] kann man danach entsorgen, das braucht man nicht mehr. Wenn man eine „alle-sind-fertig“ Kontrolle haben möchte sollten sich fertige Sortier-threads bei DATA abmelden.)

Das ganze geht auch noch eleganter, aber dann sollte man sich definitiv GUT mit Threads auskennen.

Wenns zu schnell war poste nochmal. Dann will ich aber auch wissen welche Uni/Fachhochschule die Frage gestellt hat.

cu

Die Threads sind zur Visualierung von Sortieralgorithmen da, dem Benutzer soll eine parallele Ausführung :dieser angezeigt werden. Diese sollen aber nicht nur selbständig ablaufen, sondern auch schrittweise vom :Benutzer gesteuert werden.

Also 2 Varinaten: einmal stop&Go und einmal „durchlaufen bis
zum ende“

genau… die dritte Variante ist ein Timer, aber der macht ja nichts anderes als automatisiertes „stop&Go“

Ohne dir auf die Füsse tretten zu wollen: du hast nocht nicht
viel mit Threads gearbeitet, oder ? Der AWT-thread sagt dir
auch nix ? Es ist eine Hausaufgabe ? 3x ja ?

naja, zumindest kenn ich mal die Methoden, die ein Thread so alles bietet und ich weis dass „suspend()“ und „resume()“ für meine Zwecke perfekt wären, tja, deprecated… AWT-Thread ist der GUI-Thread? Es handelt sich um eine Studienarbeit, sagen wir, du lagst zu 75% richtig :wink:

Das mit dem „auf’s-fertg-werden-warten“ ist einfach, dafür
gibts Object.wait() (und notifyAll()). ABER::open_mouth:K, ich hol jetzt weiter etwas aus und beschreibt eine
komplexere und „korrektere“ Lösung,… da es eine Hausaufgabe
ist werd ich dir aber keine Komplett-lösung hinschreiben.

Du willst Threads starten die was berrechen und später die
Werte zusammensuchen und darstellen. Die Quiz-Frage ist:
welcher Thread sammelt die Werte ein und wie weiss er wann die
anderen fertig sind ?

Ich habe deinen Ansatz jetzt nicht 100%ig verstanden, was hältst du davon:

An der Stelle im Alg-Code, wo pausiert werden soll, wird folgende Funktion aufgerufen: das Flag Debug wird true gesetzt, wenn schrittweises Vorgehen gefordert ist, Suspended wird von außen gesetzt und nach Ende der Visualisierung verändert.

protected void checkExecution() {
if (fThreadDebug == true) {
try {
Thread.currentThread().sleep(1); // sleep interval in ms
synchronized (this) {
while (fThreadSuspended) {
wait();
}
}
}
catch (InterruptedException ex) {
System.out.println(„InterruptedException“ + ex.toString());
}
fThreadSuspended = !fThreadSuspended; // change state so the exection will be stopped again
}

Es gibt zwei Objektarrays, eins zur Visualisierung und eines zur Berechnung. Die Steuerung übernimmt das GUI, d.h. berechnen->visualisieren->warten auf weiter (manuell o. automatisch) [notify!]->berechnen und so weiter… Hier werden auch die Werte aus dem Sort-Objekt ausgelesen und dem Vis-Objekt übergeben, durch repaint() [was hat die 10 zu bedeuten?] wird das Zwischenergebnis dargestellt.
Somit wartet der AWT-Thread ja eigentlich nicht.

Du baust dir ein zentrales Objekt DATA das alle Werte bekommt.
In dem Object geben die Sortier-threads ihre Werte ab und
kucken ob sie weitermachen sollen oder nicht. (Polling)

So in der Art

public synchronized boolean lastValues (int[] aktuelles_array,
int SortiererID)

Also soll die Sortier-Funktion diese Funktion aufrufen? Die folgenden Schritte konnte ich leider nicht mehr nachvollziehen:

(Das int[]-array sollte eine Kopie sein oder kopiert werden.
Nicht einfachso im Rechenthread und in DATA das gleiche Array
verwenden)

Wenn das false zurückgibt soll sich der Thread erstmal
schlafen legen, und zwar in DATA.wait(). Klickt der User auf
„noch einen Schritt rechnen“ (oder "ganz-zuende-rechenen)
rufts du DATA.notifyAll() auf. Dadurch laufen die
sortier-threads wieder weiter. Wenn das Prog eh von anfang an
durchlaufen soll kann die Methode auch immer true zurückgeben.
Bei true sollen die Threads nicht warten und nicht „wait()“
aufrufen.

Damit wären die Daten schonmal threadsafe eingesammelt und die
GUI funktioniert auch noch.

Bleibt noch das anzeigen:
Jetzt kommt wieder ein dummes Problem mit dem AWT-Thread: man
kann in lastValues sich einfachso einen graphic-kontext
(graphics2D-Object) besorgen und die Darstellung aktualiserien
ABER:

Das führt fast immer zu Darstellungsfehlern. Deshalb gibt es
„repaint()“ und „invokeLater(Runnable R)“. Für deine Zwecke
(und eine gute Note) reicht „repaint(10)“. Du lässt DATA von
JComponent erben und überschreibst (überlädest… wie sagt man
das im Deutschen ?) „public synchronized void paint(Graphics
g)“. (Das synchronized ist wichtig. Ohne wirst du diverse
Multi-thread-Probleme aus dem 1-3 Semster in der Praxis
kennenlernen) In der Methode malst du auf Basis der aktuellen
Werte die Diagramme neu. paint darf nur im AWT-Thread
ausgeführt werden… den man ja nicht aufhalten soll. Also
ruft man ihn in lastValues mit „DATA.repaint(10)“. Daraufhin
versucht der AWT-Thread möglichst bald „DATA.paint(Graphics
g)“ aufzurufen.

Was das Starten der Threads angeht:

… User hat festgelegt welche Methoden er sehen will …
Runnable[] R = new Runnable [Anzahl_der_sortierer];

for (…){
bestimme zuständige Klasse/subklasse
initalisiere sie
trage sie in R[i] ein.
}
for (…){
R[i].start();
}

Wieso mit 2 Schleifen: „initalisiere sie“ wird etwas länger
dauern als start(), die ersten Threads hätten somit einen
erheblichen Zeitvorsprung bekommen. Deshalb 2 Schleifen.

Ich will kein Rennen veranstalten, welcher Alg schneller ist, sondern darstellen, welcher am wenigsten Schritte benötigt und effektiv mit Moves und so umgeht. Das ist insofern problematisch, da das ja von Hand festgelegt werden muss…

Wenns zu schnell war poste nochmal. Dann will ich aber auch
wissen welche Uni/Fachhochschule die Frage gestellt hat.

Ja, war zu schnell :smile:
ne BA in Ba-Wü (kennt sowas überhaupt jnd??), mehr gibt’s nicht, sonst weiß mein Prof ja sofort, dass ich dazu unfähig war *gg*

Gruß
Marc

Moin

naja, zumindest kenn ich mal die Methoden, die ein Thread so
alles bietet und ich weis dass „suspend()“ und „resume()“ für
meine Zwecke perfekt wären, tja, deprecated…

Sie zerstören die synchronisation. Wenn irgendwo synchronized im Prog vorkommt: tus nicht.

AWT-Thread ist der GUI-Thread?

genau.

Ich habe deinen Ansatz jetzt nicht 100%ig verstanden, was hältst
du davon:

protected void checkExecution()  {
if ( fThreadDebug == true) {
try {
Thread.currentThread().sleep(1); // sleep interval in
ms
synchronized (this) {
while (fThreadSuspended ) {
wait();

 Um das wait muss ein zweiter try-catch-block rum, sonst läuft der Thread bei einem notify()  weiter auch wenn fThreadSuspended noch true ist.

catch (InterruptedException ex) {
System.out.println(„InterruptedException“ +
ex.toString());
}
fThreadSuspended = !fThreadSuspended; // change state so
the exection will be stopped again

wenn da oben kein try-catch ist… geht das schief.

Es gibt zwei Objektarrays, eins zur Visualisierung und eines zur
Berechnung. Die Steuerung übernimmt das GUI, d.h.
berechnen-:visualisieren-:warten auf weiter (manuell o.
automatisch) [notify!]-:berechnen und so weiter… Hier werden
auch die Werte aus dem Sort-Objekt ausgelesen und dem Vis-Objekt
übergeben, durch repaint() [was hat die 10 zu bedeuten?]

maximal 10 ms warten vor dem repaint(). (Damit das Ding nicht all 0.5 ms anfängt was neues zu malen.

Somit wartet der AWT-Thread ja eigentlich nicht.

Wenn der GUI-Thread nur die Variablen fThreadDebug und fThreadSuspended  setzt und nie checkExecution()  aufruft: ja, past, kann man so machen.

:stuck_out_tongue:ublic synchronized boolean lastValues (int[] aktuelles_array,
:int SortiererID)

Also soll die Sortier-Funktion diese Funktion aufrufen?

Die Sortier-threads,. genau. Damit die aktuellen Werte dem GUI-Thread zur Darstellung bereitstehen. Die Methode entspricht deiner checkExecution(), nur dass ich die Variablen zentral halten wollte.

Ich will kein Rennen veranstalten, welcher Alg schneller ist,
sondern darstellen, welcher am wenigsten Schritte benötigt und
effektiv mit Moves und so umgeht.

Dann darfst du nicht mehrere sortier-Threads benutzen, dann reicht einer.

cu