# Vererbung, Reflection und automatischer Methodenaufruf



## danowar (16. Dez 2010)

Hallo zusammen,

ich hab schon im Forum gestöbert und bin bis jetzt leider nicht fündig geworden und bei Google scheine ich die falschen Begriffe zu wählen, deswegen hier meine Frage:

Ich habe folgende (vereinfachte) Klassenstruktur:

- eine Klasse Element
- jedes Element hat einen Parent vom Typ Element
- u.a. eine Klasse Node, die von Element erbt
  - diese hat mehrere Methoden "add", jeweils mit einem Parameter unterschiedlichen Typs
- manche Unterklassen von Element können Kinder haben und besitzen entsprechend verschiedene Listen für die verschiedenen Kindertypen.

Nun habe ich eine Container-Struktur, der ich einfach nur sagen will "füge dieses Element hinzu und erledige den Rest."

D.h. eine Methode addElement(Element newElement), die
- das Element in die Liste übernimmt
- abhängig von der Klasse von newElement die entsprechende add-Methode im Parent von newElement aufruft.

Das zweite davon ist nun mein Problem. Ich will nicht zwanzigtausend mal instanceof verwenden, sondern automatisch den Parent in die richtige Klasse casten, das Element newElement in die richtige Klasse casten und die richtige add-Methode automatisch aufrufen.

Ich hoffe, das ist gut genug erklärt.

Wer kann mir auf die Sprünge helfen?


----------



## Andi_CH (16. Dez 2010)

Wenn ich dich richtig verstanden haben geht das automatisch richtig. Egal was die Referenz für einen Typ hat, es wird das doIt() des Obejektes ausgeführt.
(Hat mich zwar ehrlich gesagt auch überrascht)


```
public class Test1 {
	public void doIt() {
		System.out.println("Test1");
	}
}
```


```
public class Test2 extends Test1 {
	public void doIt() {
		System.out.println("Test2");
	}
}
```


```
public static void main(String[] args) {
	Test1 t1 = new Test1();
	Test1 t2 = new Test2();
	t1.doIt();
	t2.doIt();
}
```
Output:

```
Test1
Test2
```


----------



## danowar (16. Dez 2010)

Mmhh...da sind dann noch keine verschiedenen Parametertypen dabei.

Hier mal in Code meine Hierarchie:


```
public abstract class Element {
private Element parent;

public Element getParent() {
   return parent;
}
}
```


```
public class AnderesKind extends Element;

public class Node extends Element {
private HashSet<Node> nodes;
private HashSet<AnderesKind> andereKinder;

public boolean add(Node newNode) {
   nodes.add(newNode);
}
public boolean add(AnderesKind newKind) {
   andereKinder.add(newKind);
}
```


```
public class EinElementContainer {
private HashSet<Element> alleElemente;

public boolean add(Element newElement) {
   alleElemente.add(newElement);
   newElement.getParent().add(newElement);
}
}
```

Das letzte soll halt automatisch casten und die richtige Methode aufrufen.


----------



## Michael... (16. Dez 2010)

Andi_CH hat gesagt.:


> (Hat mich zwar ehrlich gesagt auch überrascht)


Nennt sich Vererbung ;-)

@danowar: Dein Code macht m.E. keinen Sinn. Kannst Du mal an einem konkreten Bsp. beschreiben was Du genau vorhast?

Wenn an einem Objekt vom Typ Element eine Methode aufgerufen werden soll, muss diese mindestens abstract in der Superklasse definiert sein.


----------



## danowar (16. Dez 2010)

Michael... hat gesagt.:


> Nennt sich Vererbung ;-)
> 
> @danowar: Dein Code macht m.E. keinen Sinn. Kannst Du mal an einem konkreten Bsp. beschreiben was Du genau vorhast?
> 
> Wenn an einem Objekt vom Typ Element eine Methode aufgerufen werden soll, muss diese mindestens abstract in der Superklasse definiert sein.



Mein Gedankengang:

- jedes Mal, wenn ich ein Element in den ElementContainer hinzufüge, ist es kein Element, sondern etwas konkretes (habe mal Element abstrakt gemacht zum verdeutlichen).

- d.h. der Parent eines Elements (z.B. eine konkrete Node) soll das neue Element (z.B. eine konkrete Instanz von AnderesKind) in die entsprechende Liste aufnehmen.

- es ist also nur eine Referenz auf Element vorhanden, eigentlich soll aber Node.add(AnderesKind) ausgewählt werden.


Konkret...bastele ich an einem Modell mit verschiedenen Modellelementtypen, die alle von Element abgeleitet sind. Dabei können manche Elementtypen halt Subelemente beinhalten und manche nicht.
Diese, die es können, können mehrere verschiedene Elementtypen aufnehmen, die separat gespeichert werden sollen, damit einfacher Zugriff darauf möglich ist.


----------



## Andi_CH (16. Dez 2010)

Michael... hat gesagt.:


> Nennt sich Vererbung ;-)



Wenn die Referenz Test1 hab ich erwaret dass auch Test1.doIt() ausgeführt wird...

Ich halte das selbst für eine sehr schräge Idee, aber gibt es eine Möglichkeit von Aussen sozusagen super.doIt() aufzurufen?


----------



## Michael... (16. Dez 2010)

Andi_CH hat gesagt.:


> Ich halte das selbst für eine sehr schräge Idee, aber gibt es eine Möglichkeit von Aussen sozusagen super.doIt() aufzurufen?


Nein, wüsste jetzt allerdings auch nicht für was das gut sein sollte.

@danowar: Hab Deinen Gedankengang nicht wirklich verstanden. Kann man das mittels simplem kompilierbarem Code und mit instanceof/casten darstellen?


----------



## danowar (16. Dez 2010)

Ich glaube...daß es jetzt vielleicht geht. Hier mein momentaner Code:


```
public final boolean addElement(final Element newElement) {
		allElements.add(newElement);
		boolean invocationSuccessful = false;
		Element theParent = newElement.getParent();
		try {
			for (Method m : theParent.getClass().getMethods()) {
				if (m.getName().equals("add")) {
					for (Class<?> c : m.getParameterTypes()) {
						if (c == Element.class) {
							invocationSuccessful = (Boolean) m.invoke(theParent, newElement);
						} else if (c == newElement.getClass()) {
							invocationSuccessful = (Boolean) m.invoke(theParent, newElement);
						}
					}
				}
			}
			throw new NoSuchMethodException("Tried to add a child " + newElement.getClass().getSimpleName() + " not supported by the parent " + theParent.getClass().getSimpleName() + ".");
		}
		catch (NoSuchMethodException x) {
			x.printStackTrace();
		}
		catch (IllegalAccessException x) {
			x.printStackTrace();
		}
		catch (InvocationTargetException x) {
			x.printStackTrace();
			}
		finally {
			if (!invocationSuccessful) {
				allElements.remove(newElement);
			}
		}
		return invocationSuccessful;
	}
[/Java]

@Michael...:

Vorher war das ein Gewurschtel mit :

[Java]
if (theParent instanceof Node) {
   ((Node) theParent).add(newElement);
} else if (theParent instanceof AnderesKind) {
   ((AnderesKind) theParent.add(newElement);
} 
[...]
```

Plus das casten von newElement in die richtige Klasse, weil "theParent hat keine Methode add(Element)"


----------



## KSG9|sebastian (16. Dez 2010)

Ich glaube du gehst das Problem falsch an.

Du hast für jeden Subtyp eine eigene Liste zum verwalten, richtig?
Warum das? Nimm einfach eine Liste welche Elemente aufnimmt und alles kommt dort rein. Der Typ ist dir doch an der Stelle egal.

Alternativ kannst du natürlich folgendes machen:


```
Map<String, Set<? extends Element>> items = new HashMap<String, Set<Element>>();
items.put(Node.class.getName(), new HashSet<Node>());
items.put(KonkreteNode.class.getName(), new HashSet<KonkreteNode>());
items.put(KonkretereNode.class.getName(), new HashSet<KonkretereNode>());

public void add(Element e){
   items.get(e.getClass().getName()).add(e);
}
```

Damit kannst du pro konkreter Klasse eine eigene Liste verwalten. Schön ist das allerdings nicht und lässt eher auf ein Designproblem schließen..


----------



## tfa (16. Dez 2010)

```
if (c == Element.class) {
                            invocationSuccessful = (Boolean) m.invoke(theParent, newElement);
                        } else if (c == newElement.getClass()) {
                            invocationSuccessful = (Boolean) m.invoke(theParent, newElement);
                        }
```
Wo ist denn der Unterschied zwischen Zeile 2 und Zeile 4?


----------



## KSG9|sebastian (16. Dez 2010)

Schau mal meinen vorherigen Beitrag an.


Und..was tust du denn da? Was willst du mit dem Code bezwecken.
Poste mal allen relevanten Code damit wir verstehen was du da vor hast...


----------



## danowar (16. Dez 2010)

Das ganze soll ein rudimentäres Abbild des Metamodells von UML sein.

- alle Elemente haben Parents, d.h. Elemente, die sie beinhalten.
 (z.B. ein Package hat Kinder, eine Component hat eine Node als Parent)

- Der Container ist das Modell bzw. das Diagramm an sich, dieses speichert erstmal alle Elemente im Diagramm in einer Liste.

- In jeder nicht-abstrakten Klasse können verschiedene Arten von Kindern vorhanden sein, z.B. kann eine Node sowohl Komponenten als auch selber Nodes enthalten.

- Jetzt soll automatisch beim hinzufuegen eines Elements in das Diagramm (addElement) die Verbindung Child->Parent hergestellt werden. Im Child-Element steht beim erstellen der Parent, im Parent muss mit einer add-Methode das Kind hinzugefügt werden.

WICHTIG: Wir haben das Metamodell neu erstellt, weil eine vorhandene Darstellung dessen bestimmte Funktionalität nicht ergab.


----------



## danowar (16. Dez 2010)

tfa hat gesagt.:


> ```
> if (c == Element.class) {
> invocationSuccessful = (Boolean) m.invoke(theParent, newElement);
> } else if (c == newElement.getClass()) {
> ...



Zeile 2 ist dafür da, wenn das Parent-Element keine spezielle Liste (z.B. eine Liste für Nodes) hat, sondern nur eine Liste mit Elementen. Dann gibt es eine Methode add(Element). Die soll dann benutzt werden.

Zeile 4 ist für die Fälle von speziellen Listen für Kinder (z.B. eine Liste für Nodes).
Eigentlich sollte da auch ein cast stehen (newElement.getClass().cast(newElement))


EDIT: Wahrscheinlich wäre es wirklich besser, eine allgemeine Liste zu haben, eine Methode add(Element) und Methoden, die auf der allgemeinen Liste iterieren und die einzelnen Typen-Sammlungen zurückgeben.

Dann wäre da noch der Fall, das ein Element sowohl Kinder haben kann als auch Verbindungen zu anderen Elementen. Z.B. eine Node hat einen oder mehrere Links.


----------



## danowar (16. Dez 2010)

Aller guten Dinge sind drei:

Wie wäre es im Design mit:

- Element (hat ein Parent)
- ParentElement (hat Kinder bzw. Liste von Kindern)
- ConnectedElement (hat Verbindungen bzw. eine Liste von Verbindungen)

EDIT: Ich hasse es, mit anderer Leute Designs zu arbeiten, die dann auch noch scheinbar fürs Klo sind.


----------



## KSG9|sebastian (16. Dez 2010)

Ich versteh immer noch nicht was du vor hast...

Wozu ParentElement?


```
class Element{
  private Map<Element, Element> connections;
  private Element parent;
  private Set<Element> children;

  public boolean isRoot() { return parent == null; }
  public boolean hasChildren() { return !children.isEmpty()  }
}
```

Damit kannst du doch all das abfackeln ?!
Konkrete Klassen können doch die Methoden aus der Superklasse verwenden. Falls noch Einschränkungen notwendig sind kannst du noch abstrakte Methoden wie "isChildrenAllowed()" o.ä. einfügen


----------



## danowar (16. Dez 2010)

Mir wurde gesagt, dass es furchtbar schlechtes Design wäre, wenn man eine Variable in einer Klasse hat, die in manchen Instanzen immer null ist, und daß dafür Vererbung da wäre.

Deswegen ParentElement und ConnectedElement, weil erst die dann...

ParentElement
- children

ConnectedElement
- connections

enthalten würden.

Was stimmt denn nun?


----------



## Wildcard (16. Dez 2010)

Ist jetzt schon das vierte mal heute das ich auf EMF verweise, aber anstatt n Listen zu Pflegen oder eine Liste und dann n verschiedene Sublisten teuer daraus abzuleiten wird soetwas in EMF elegant mit einer FeatureMap gelöst.
http://www.eclipse.org/modeling/emf/docs/overviews/FeatureMap.pdf
Wenn man aus irgendeinem Grund EMF nicht verwenden möchte würde ich bei einer Liste für die Kinder bleiben und die anderen Listen daraus mit dem Google Collection Framework und einem Iterables Filter ableiten, allerdings können die abgeleiteten Listen dann anders als bei EMF nicht mehr direkt manipuliert werden.


----------



## KSG9|sebastian (16. Dez 2010)

Schlechtes Design wenn Variablen null sind? Sorry, aber was ist das für ein Quatsch.

Mir fällt spontan keine einzige Klasse aus dem JDK ein in der nicht irgend eine Variable mal null sein kann.
Ich finde es ist furchtbar schlechtes Design wenn Vererbung für alles und jeden mißbraucht wird.

Wenn du dein Problem mal genauer beschreibst und eher auf den fachlichen Nutzen können wir dir sicher helfen. Aber dazu bräuchten wir die Anforderungen, keine Codeschnipsel aus denen kein wirklicher Sinn ergeht. 

Ich kann dir nur jetzt schon sagen das Reflection mit Sicherheit der falsche Weg ist


----------



## Wildcard (16. Dez 2010)

> Schlechtes Design wenn Variablen null sind? Sorry, aber was ist das für ein Quatsch.
> 
> Mir fällt spontan keine einzige Klasse aus dem JDK ein in der nicht irgend eine Variable mal null sein kann.
> Ich finde es ist furchtbar schlechtes Design wenn Vererbung für alles und jeden mißbraucht wird.


Also wenn beispielsweise nur bestimmte Typen von Objekten überhaupt Connections haben können, oder nur bestimmte Typen von Objekten überhaupt Kinder haben können, dann fände ich es auch falsch zu versuchen alle über einen Kamm zu scheren.



> Ich kann dir nur jetzt schon sagen das Reflection mit Sicherheit der falsche Weg ist


Das kann ich nur unterschreiben. Hier mit Reflection zu arbeiten wäre ein grottiges Design.


----------



## KSG9|sebastian (17. Dez 2010)

Wildcard hat gesagt.:


> Also wenn beispielsweise nur bestimmte Typen von Objekten überhaupt Connections haben können, oder nur bestimmte Typen von Objekten überhaupt Kinder haben können, dann fände ich es auch falsch zu versuchen alle über einen Kamm zu scheren.



Ja, das schon. Ich hab es aber so verstanden da es Nodes geben kann welche "connected" sind, dass es Nodes geben kann welche Root sind und das es Nodes gibt welche ein Parent für andere sind, daher spricht nix gegen nur eine Klasse.
Wenn es natürlich so ist das eine Variable dauerhaft für alle Instanzen null ist dann würde ich auch zu Vererbung oder EMF o.ä. greifen.


----------



## Wildcard (17. Dez 2010)

> Ja, das schon. Ich hab es aber so verstanden da es Nodes geben kann welche "connected" sind, dass es Nodes geben kann welche Root sind und das es Nodes gibt welche ein Parent für andere sind, daher spricht nix gegen nur eine Klasse.


Ich war nach dem lesen auch nicht sicher wie die Sache zu verstehen war. In dem Fall hättest du natürlich Recht, dann gäbe es auch kaum andere Möglichkeiten wenn man nicht ständig Objekte verwerfen möchte.


----------



## danowar (20. Dez 2010)

Also...jetz mal klipp und deutlich, was das Problem ist (hoffe ich...)

In einem schon existenten Tool, welches ich nicht verändern darf (grundsätzlich keine Zeit dafür und nicht meine Aufgabe) wird die Netbeans MetaData Repository (NBMDR) benutzt, um mit einem UML-Metamodell Instanzen von UML-Modellen innerhalb des Tools zu erstellen, die dann durchlaufen und durchsucht werden können.
Eine entwickelte Erweiterung von UML (also Stereotypen und TaggedValues) wird in Modellinstanzen benutzt, um verschiedene Eigenschaften zu untersuchen. So weit, so allgemein.

EDIT: Zu EMF, das Tool soll wohl irgendwann in nicht allzuferner Zukunft auf EMF umgestellt werden. Allerdings nicht jetzt und garantiert nicht durch mich.

Schön und gut, allerdings ist dieses NBMDR scheinbar nicht für eine neuere UML-Erweiterung geeignet. In dieser neuen Erweiterung soll auf Kopien von Modellinstanzen gearbeitet werden, da diese zur Laufzeit geändert werden. Mehrere dieser Kopien werden benötigt, weil mehrere mögliche Durchläufe nötig sein können, bis eine korrekte Änderung gefunden wird. NBMDR kann (scheinbar) keine Klone von der Modellinstanz erstellen. Auch ist NBMDR nicht transparent, was die Änderung des Modells angeht. Wenn ich irgendwo eine Methode aufrufe, die ein Modellelement ändert, weiss ich nicht, was alles noch im Hintergrund erledigt wird, wenn z.B. beidseitig Verbindungen angepasst werden müssen (z.B. Vater zu Kind und umgekehrt).

Ausserdem ist eine Anforderung, dass das Erstellen von Elementen nur halbseitig durchgeführt wird, ich sage also zuerst nur, daß ein neues Element diesen Parent hat. Der Parent hat das neue Kind allerdings noch nicht eingetragen. Kann NBMDR (scheinbar) auch nicht. Entweder drin oder nicht drin.

Also wurde eine neue Modellierung von UML-Elementen designt, die die Eigenschaften unterstützt. Dieses Design ist wohl suboptimal, wie oben gesehen. Also entweder nachbessern oder mit dem Hammer draufhauen, bis es passt. (Reflection == Hammer)

Die Spezifikation, die für das Design verwendet wurde, ist *UML 1.5*.

Es wird wohl darauf hinauslaufen, daß ich noch am Design feilen werde.

Zu den obigen Annahmen:

Grundsätzlich hat jedes Element in unserem Metamodell von UML einen Parent, d.h. ein Element, dem es untergeordnet ist. Das wäre z.B. eine Component in einer Node, eine Methode in einer Klasse, o.ä.
"Speziellere" Sachen wie z.B. Assoziationen haben dann halt das Package, in dem sie sind, oder das Modell selber als Parent.

Ein Element hat also ein Parent.
Manche Elemente (z.B. Nodes, Packages) können Kinder (enthaltene Elemente) haben. Z.B. kann (!) eine Node Kinder haben (Components z.B.), muss aber nicht (wäre dann aber sehr rudimentär modelliert).
Also:
- Node 0..* KindElement
- Package 0..* KindElement

Andere (z.B. Dependencies) haben keine Kinder.

Manche Elemente (z.B. Nodes, Klassen) können Verbindungen haben (z.B. Assoziationen, Dependencies), müssen aber nicht.
Also:
- Node 0..* Assoziation


Ich persönlich würde jetzt sagen, daß Elemente, die Kinder haben können, grundsätzlich eine Liste der Kinder enthalten (z.B. ein HashSet, da das selbe Element nicht zweimal Kind im gleichen Element sein kann), diese Liste aber bei Elementen, die gerade mal keine Kinder haben, leer ist.
Also nicht "null".

Genauso mit Elementen, die Verbindungen haben könnten.

Im Moment haben wir in etwa diese Hierarchie:
- Element
- ElementMitTaggedValues extends Element
- ElementMitStereotypen extends ElementMitTaggedValues
- Stereotyp extends ElementMitTaggedValues
- "alle anderen möglichen Elemente" extends ElementMitStereotypen

, wobei "alle anderen möglichen Elemente" in sich noch etwas komplizierterer unterteilt ist.

Jetzt denke ich inzwischen (auch mit der Hilfe von diesem Forum), dass vielleicht eine etwas andere Hierarchie besser wäre.
- Element
- ElementMitKindern extends Element
- ElementMitVerbindungen extends Element

Dies im Zusammenhang mit obigen Klassen führt allerdings zu Vererbungsproblemen (so denke ich), die wegen Mehrfachvererbung "nur" mit Interfaces und mehrfachem ausimplementieren von Methoden wie z.B. getTaggedValues oder getKinder verbunden sind.

Sehe ich da richtig?


----------



## Wildcard (20. Dez 2010)

> EDIT: Zu EMF, das Tool soll wohl irgendwann in nicht allzuferner Zukunft auf EMF umgestellt werden. Allerdings nicht jetzt und garantiert nicht durch mich.


Also das würde ich mir genau überlegen. EMF unterstützt alle diese Anforderungen, inklusive dem tiefen Clonen von Modellobjekten. Das Ecore zu definieren geht wesentlich schneller alles in Java Interfaces und Implementierung zu gießen, und aus dem Ecore lassen sich dann die Interfaces und Klassen generieren.
Es gibt übrigens auch ein (ziemlich mächtiges und komplexes) fertiges EMF Modell das AFAIK alle UML2.0 Features unterstützt, falls das weiterhilft.
http://www.eclipse.org/modeling/mdt/?project=uml2


----------



## Landei (21. Dez 2010)

danowar hat gesagt.:


> Jetzt denke ich inzwischen (auf mit der Hilfe von diesem Forum), dass vielleicht eine etwas andere Hierarchie besser wäre.
> - Element
> - ElementMitKindern extends Element
> - ElementMitVerbindungen extends Element



Ich würde keine Extra-Klasse für Elemente mit Kindern machen. Stattdessen würde ich Element iterable machen, und bei der Default-Implementierung einen leeren Iterator zurückliefern. Elemente mit Kindern könnten die Methode überschreiben.


```
public abstract class Element extends Iterable<Element>{
   private final static Iterator<Element> EMPTY_ITERATOR = new Iterator<Element>(){
       public boolean hasNext() { return false; }
       public Element next() { throw new NoSuchElementException(); }
       public void remove() { throw new UnsupportedOperationException(); }
   };
   
   public Iterator<Element> iterator() { return EMPTY_ITERATOR; }
}
```


----------



## KSG9|sebastian (21. Dez 2010)

Das ist wieder so ein Punkt bei dem man natürlich drüber streiten kann das mit Vererbung zu lösen. Vielleicht hilft hier eher ein Decorator weiter.
Bei allen anderen Punkten würde ich mich sehr genau überlegen ob es lohnt dafür eine Vererbung zu machen oder das ganze anders zu lösen.

Genau genommen bist du nämlich mit obigem Design unter Umständen wieder beim "Vererbungsproblem". tfa hat hierzu einen sehr guten Beitrag geschrieben: http://www.java-forum.org/707149-post19.html

Auch wenn ich EMF teilweise skeptisch gegenüberstehe bin ich hier absolut der Meinung von Wildcard. 90% von deinen Anforderungen kannst du mit EMF abdecken, die restlichen 10% erfordern nicht übermäßig viel Arbeit...


----------

