Generische Datentypen + Baumstruktur

White_Fox

Top Contributor
Mahlzeit

Ich will eine Baumstruktur erstellen, und habe dafür eine abstrakte Klasse TreeItem geschrieben. Ein TreeItem kennt seinen Parent und seine Children und seine UUID. Vom Parent kann es ein Address-Objekt (das ist im wesentlichen eine ArrayList<UUID> mit allen UUIDs von der Baumwurzel bis zum Ziel-Child) anfordern, diese Anforderung wird nach oben bis zur Baumwurzel weitergereicht und über ein Dekoratormuster wird ein Address-Objekt ab der Baumwurzel gebaut und zum Aufrufer durchgereicht.

So können jetzt neue Child-Objekte hinzugefügt, existierende zurückgegeben oder entfernt werden, indem das lediglich das Wurzel-TreeItem mit einem Address-Objekt aufgerufen wird.

Das funktioniert soweit sehr gut.

Jetzt möchte ich ein Modell des Baums erstellen, um nicht jedesmal den gesamten Baum bzw. das gesamte TreeItem herumreichen muß, wenn ich z.B. lediglich die Namen der TreeItems in einer Baumstruktur anzeigen lassen will. Außerdem ist die Baumstruktur von außen nicht ersichtlich. da der Baum ja nur über das Wurzel-Element über ein Address-Objekt benutzt werden soll-und dieses Address-Objekt braucht man ja erstmal.

Jetzt ist mein Plan, eine (abstrakte) Klasse TreeItemModel zu schreiben. Ein TreeItemModel soll seinen Parent (bzw. den Parent des "Origials" im Baum) kennen und ein aktuelles Address-Objekt enthalten. Vielleicht soll es auch die Children kennen, ich weiß aber noch nicht ob ich das brauche.
Am Ende gebe ich eine ArrayList<TreeItemModel> aus, die alle TreeItemModels des Baums enthält. Da jedes TreeItemModel ein Address-Objekt hat und es das TreeItemModel des Parents kennt, kann ich die Baumstruktur in einer einfachen Schleife wiederherstellen.

TreeItem ist abstrakt, weil diese Klasse eine Fähigkeit bereitstellen soll. Ich hätte es lieber über ein Interface gelöst, aber mit einem Interface kann man keine Befähigung umsetzen da Interfaces keine Instanzvariablen aufnehmen können. Aber ein TreeItem nur um des TreeItems willen nutzt noch nichts, da soll noch mehr rein um es nutzen zu können.
Daher ist auch TreeItemModel abstrakt. Diese Klasse soll ja einerseits die Nachvervolkbarkeit der TreeItems bereitstellen, andererseits auch als Nutzdatencontainer dienen.

Jetzt überlege ich, wie ich das am Besten umsetze. Ich weiß jetzt noch nicht, wieviele und welche TreeItems es mal geben wird und welche Daten ich dann haben will.

Ich hab schon überlegt, das TreeItemModel nicht abstrakt zu machen und dem TreeItem dafür noch eine abstrakte Methode mitzugeben. Diese dient dazu, eine HashMap aller gewünschten Eigenschaften zurückzugeben. Mit einer Methode wie "Object getSomeProperty(String propertyname)" würde ich die Werte dann aus dem TreeItemModel rausholen.

Diese Lösung gefällt mir jedoch nicht. Ich habe diese Methode nur einmal, und müßte diese jedesmal erweitern wenn ich dann doch einmal andere Daten brauche. Und dann schleppe ich jedesmal ALLE Daten umher die irgednwann irgendwie mal gebraucht werden könnten -> schlechte Kapselung.

Am liebsten wäre mir, für einen konkreten Anwendungsfall eine Klasse zu schreiben, die von der Klasse TreeItemModel erbt und der ich, je nach Wunsch, die Nutzdaten mitgeben kann die ich jeweils haben will. Und hier fangen die Probleme an.

1. Wie kriege ich ein TreeItem-Objekt dazu, nach Wunsch ein TreeItemModelABC oder ein TreeItemModelXYZ zurückzuliefern? Über eine Enum als Methodenparameter wäre vielleicht eine Möglichkeit, aber so richtig gefällt mir die Idee nicht.

2. Besser wäre, dem TreeItemModel das Original im Konstruktor bekannt zu machen. Aber da stellt sich die Frage: Woher kann ein TreeItem wissen, ob es ein TreeItemAnton oder ein TreeItemBerta ist? Und wenn ich nachher ein ArrayList<TreeItemModel> habe: wie kriege ich möglichst einfach (also ohne Reflections-Brechstange oder Castorgie) raus, ob list.get(5) dann ein TreeModelAnton oder ein TreeModelBerta zurückliefert? Ich hab überlegt ob da nicht was mit Generics zu machen wäre.

Hat jemand eine Idee (vielleicht auch einen komplett anderen Ansatz), wie ich da am Besten zu einer guten Lösung komme?
 

mihe7

Top Contributor
Kannst Du Dein Vorhaben mal an einem Beispiel, vielleicht am Dateisystem, verdeutlichen? Mir ist das zu abstrakt :)
 

White_Fox

Top Contributor
Beispiel...ich versuchs mal (der konkrete Anwendungsfall ist noch etwas unausgegoren, da brauche ich eine andere Metapher):

Nehmen wir an, du willst Bücher speichern. Den Büchern soll es dabei völlig egal sein, wie sie sortiert werden, du willst aber auf jedes Buch zugreifen können.

Die Bücher sind in Regalen sortiert.

Die Regale stehen in verschiedenen Räumen. Auch Räume können einzelne Bücher enthalten.

Die Räume befinden sich in Häusern.

Die Häuser befinden sich sich in einer Stadt. Auch in einer Stadt können einzelne Bücher herumliegen.


Die Klassen Stadt, Haus, Raum, Regal und Buch werden im Baum in einer passenden Struktur gehalten und erben deswegen von TreeItem. Vom Wurzelobjekt möchte ich jetzt eine ArrayList<TreeItemModel> haben. Die Klasse "class BuchModel extends TreeItemModel" z.B. liefert nur den Titel des Buches, aber nicht das ganze Buch. Und es liefert, wo sich das Buch gerade befindet, sodaß ich z.B. über alle TreeItemModel-Objekte iterieren kann und dann die Position der Bücher wieder als Baum darstellen kann.

Jetzt stellt sich die Frage: Wie baue ich das so, daß ich weiß, ob ich ein "class Buch extends TreeItem"-Objekt oder ein "Class Haus extends TreeItem"-Objekt habe?

Ich hätte aus TreeItem ja lieber ein Interface gemacht, aber mit diesem kann ich ja kaum was machen, bzw. müßte den ganzen TreeItem-Schnickschnack jedesmal neu implementieren. Und das will ich nicht.

Nachschlag:
Das Beispiel hinkt etwas, denn aktuell habe ich nur eine Klasse, die von TreeItem erbt, es werden später vielleicht insgesamt zwei oder drei. Die Klassen werden auch nicht ganz so krass verschieden sein wie Haus und Regal. Ich weiß da aber auch noch nicht, ob sich eine so eindeutige Hat-Ein-Beziehung aufbauen läßt wie das zwischen Haus und Regal vielleicht möglich wäre.
 

White_Fox

Top Contributor
Na wie gesagt: Gar nicht.

Ansonsten so:
Java:
public TreeItem(TreeItem parent)

private boolean itMeansMe(Address a)
private boolean iAmTheParent(Address a)

void setParent(TreeItem parent)
UUID getID()
TreeItem getParent()
void changeParent(TreeItem newParent)
Address getAddress()
TreeItem getNext(Address a)

public boolean addChild(TreeItem item, Address parent)
public TreeItem removeChild(Address item)
public TreeItem getChild(Address a)

Das sind die Methoden die ich brauche. Die meisten sind entweder private oder package private, evt. werden ein paar davon noch protected. Aber das wenigste ist public, und die Implementierung ist stets gleich.
 

httpdigest

Top Contributor
Wozu benötigst du den Unterschied, ob es sich nun um eine Stadt, ein Haus, einen Raum oder ein Regal handelt? Aktuell hört es sich an, als würde einfach nur ein TreeItem reichen, solange es eben weitere TreeItem als Kinder haben kann. Weitere differenzierte Klassen würde ich an deiner Stelle nur einführen, wenn du sie wirklich benötigst.
Was bei dir noch fehlt, ist eine top-level Beschreibung, wie ein Client/API-User deine Datenstruktur tatsächlich nutzen würde. Mir ist auch noch nicht ganz klar, welchen Anwendungsfall du mit der Abbildung des Ganzen als Baum überhaupt abdecken möchtest. Warum ein Baum? Warum muss etwas (z.B. ein Regal) in etwas anderem (z.B. einem Haus) enthalten sein? Wann ist diese Information nützlich/hilfreich?
 

White_Fox

Top Contributor
Baumstruktur deswegen weil es nachher nicht um Bücher, sondern um elektronische Bauteile (und eigentlich noch nichtmal um die, sondern darum, Bauteile zu erzeugen) geht. Und da will ich nachher alle Freiheiten haben, eine Sortierung aufzubauen.

Wenn jemand alle Bauteile in eine Bibliotheksseite reinkloppt-ok, kann er machen. Wenn er zwischen Widerständen und Kondensatoren unterscheiden will-gut. Eine Differenzierung zwischen Widerständen für Wald- und Wieseneinsatz und welchen mit MIL-Zertifizierung für Militär- und Raumfahrtanwendungen soll genauso möglich sein. Und wo ich schonmal dabei bin: Die ESA hat ein Nummerierungssystem entwickelt, daß z.B. bei Widerständen zwischen Dick- und Dünnschicht- oder Drahtwiderständen unterscheidet.

Momentant gibt es nur die Klasse LibraryPage (die Klasse gibt es schon ein Weilchen), die ich von TreeItem ableiten würde -> LibraryItem. Für die erste Version wird der Baum auch nur ein einziges Item enthalten.

Was jedoch schon geplant ist: Ich möchte z.B. auch mehrere, ich nenne es mal Bibliotheksprojekte, im Programm gleichzeitig öffnen können. Da müßte ich zwischen "Library", was eine Bibliothek mit beliebig vielen Bibliotheksseiten wäre, und "AllLibraries", was das Wurzelelement des Baums wäre, unterscheiden.

Und zum TreeModel: Jedes LibraryPage-Objekt hat einen Namen (den kann der Benutzer frei wählen, der ist und soll nicht eineindeutig sein, daher brauche ich sowieso noch etwas um zwischen verschiedenen LibraryPage-Objekten zu unterscheiden), und diese Namen möchte ich dann als Baumstruktur darstellen. Eine Klasse würde ich direkt so abrichten wollen, daß sie mir gleich ein passendes Tree-Object (JavaFX) ausspuckt.

Später will ich dann auch z.B. nur alle Library-Objekte (nicht LibraryItem!) rausfiltern. Und wer weiß, was mir später noch alles einfällt.
 

mihe7

Top Contributor
Kurz zusammengefasst: Du hast eine Menge elektronischer Bauteile, die nach gewissen Kriterien als Baum (JavaFX) dargestellt werden sollen, wobei die Bezeichnungen der Knoten vom Benutzer vergeben werden können sollen. Richtig?
 

White_Fox

Top Contributor
Nein, nicht die Bauteile selber.

Objekte, die dazu dienen, Bauteile zu erstellen. Am Ende steht eine CSV (später wahlweise auch eine Exceldatei oder ein Export in eine SQL-Datenbank), die ich Altium (Elektronik-CAD) zum Fraß vorwerfen kann. Die Daten sagen Altium dann, wo es das Schaltplansymbol, den Footprint, verlinkte Dokumente, usw. findet und was ein Bauteil noch so enthalten soll: Hersteller-Nummer, Zertifikate, Bauteilwerte, ...

Aber das enthält alles die LibraryPage-Klasse, das TreeItem kriegt von Bauteilen nichts mit. Soll es auch nicht. Eigentlich würde ich das am liebsten so bauen, daß es völlig egal ist was ich da in den Baum reinwerfe.
 

mihe7

Top Contributor
Mit geht es aktuell noch darum, die Trennung zwischen UI und Modell gedanklich nachzuvollziehen. Sorry, wenn es etwas länger dauert, ich habe das Gefühl, die Hitze macht mein Hirn träge :(

Du hast eine Menge von Objekten (zur Erstellung elektronischer Bauteile), die nach gewissen Kriterien als Baum (JavaFX) dargestellt werden sollen, wobei die Bezeichnungen der (ggf. auch nur mancher) Knoten vom Benutzer vergeben werden können sollen. Richtig?
 

White_Fox

Top Contributor
Ja, das ist korrekt.
Ich soll das vielleicht etwas genauer erklären.

Bei fast allen Bauteilen, die man so kaufen kann, setzt sich die Herstellernummer aus irgendeinem Algorithmus zusammen. Als Beispiel sei mal eine fiktive Widerstandsserie eines fiktiven Herstellers genommen.

Die ersten drei Zeichen der Hersteller-Nr
geben die genaue Serie XYZ an.

Die nächsten drei Zeichen
sind nur Ziffern, und liefern den genauen Widerstandswert: die ersten zwei Ziffern den Bauteilwert, die dritte Ziffer die Dekade.

Das nächste Zeichen
ist ein Buchstabe A oder B, der die Bauteiltoleranz angibt: 1% oder 0,1%.

Und so geht das weiter: mit Temperaturkoeffizient, Gehäusegröße, bleifrei ja/nein, Zertifizierung für spezielle Einsatzzwecke, ...

Jedes LibraryPage-Objekt enthält nun jeweils ein ComponentPage-Objekt, und ein StampPage-Objekt.
Die ComponentPage enthält die generierten Nutzdaten in Tabellenform.
Das StampPage-Objekt ist die erste "Sortiereinheit" in meinem Programm. Damit können z.B. verschiedene Widerstandsserien erzeugt werden, allerdings haben diese alle denselben Satz an Eigenschaften: alle hier erzeugten Bauteilwerte haben z.B. einen Datenblattlink, einen Bauteilwert, ein Feld für Notizen, usw.

Von der Aufteilung des LibraryPage-Objekts soll der Baum aber noch nichts mitbekommen.

Die LibraryPage-Objekte haben jedoch einen Namen, und die Namen und deren Zuordnung will ich z.B. mit dem TreeModel der View bekanntmachen.
 
Zuletzt bearbeitet:

White_Fox

Top Contributor
Wobei: Eigentlich wäre es gar nicht so schlecht, wenn auch einige Innereien der LibraryPage-Klasse mit einem Address-Objekt erreicht werden können. Eigentlich wäre das sogar großartig.

Dann gibt es allerdings noch mehr Objekte, die ich über das Modell filtern müßte. Denn diese will ich in dem Baum, die ich oben ansprach, definitiv nicht darstellen.

Edit:
Was auch gehen würde: Die Innereien eines LibraryPage-Objekts bilden einen eigenen Baum für sich. Von ganz außen, also von der LibraryPage-Baumwurzel aus kommt man dann nicht mehr ran.

Dann würde ich das ganze TreeItem-Zeugs allerdings gerne so flexibel haben, daß ich es einfach für andere Objekte verwenden kann (so war es anfangs auch angedacht).
 
Zuletzt bearbeitet:

mihe7

Top Contributor
Hm... das TreeItem-Zeugs ist doch nur (in erster Linie) für die Darstellung zuständig. Was Du da darstellt, spielt doch keine Rolle.
 

White_Fox

Top Contributor
Naja, der Benutzer soll nachher die Baumstruktur auch baumartig benutzen können. Wenn er ein Item löscht, das noch Kinder hat, dann sollen die Kinder auch weg sein.

Irgendwie sieht das hier https://stackoverflow.com/questions/450807/how-do-i-make-the-method-return-type-generic so aus, als könnte mir das helfen. Leider verstehe ich das nicht so recht.

Womit ich mich sehr gerne anfreunden würde ist, daß ich den TreeItems genau sagen kann was für eine TreeModelItem-Klasse sie zurückliefern sollen. Wenn sie eine entsprechende Klasse liefern können, liefern sie sie-wenn nicht, dann nicht.
 

mihe7

Top Contributor
Naja, der Benutzer soll nachher die Baumstruktur auch baumartig benutzen können. Wenn er ein Item löscht, das noch Kinder hat, dann sollen die Kinder auch weg sein.
Was ich damit meinte ist, dass das TreeItem das Model für die TreeView ist (Presentation Layer). Davon unabhängig ist das Domain Model zu sehen.

Wenn sie eine entsprechende Klasse liefern können, liefern sie sie-wenn nicht, dann nicht.
Java:
public class Test {
    private Object value;

    public Test(Object value) { this.value = value; }

    public <T> T getValue(Class<T> clazz) {
        if (value != null && clazz.isInstance(value)) {
            return clazz.cast(value);
        }
        return null;
    }

    public static void main(String[] args) {
        Test t = new Test("Test");
        System.out.println(t.getValue(Integer.class));
        System.out.println(t.getValue(String.class));
    }
}
 

White_Fox

Top Contributor
Ich glaube, ich verstehe allmählich, hab aber noch ein paar Fragen.

Das hier funktioniert:
Java:
abstract public <T extends TreeItemModel> T getModel(Class<T> modelClass);

Das hier funktioniert nicht:
Java:
abstract public <T extends TreeItemModel> T getModel(Class<T extends TreeItemModel> modelClass);

Das hier auch nicht:
Java:
abstract public ArrayList<<T extends TreeItemModel> T> getModel(Class<T> modelClass);

Warum nicht?
 

mihe7

Top Contributor
Java:
public abstract class Test {
    interface Value {}
    static class A implements Value {}
    static class B implements Value {}

    Object value;

    public Test(Object value) { this.value = value; }

    public abstract <T extends Value> T getModel(Class<T> clazz);

    public static void main(String[] args) {
        Test test = new Test(new B()) {
            @Override
            public <T extends Value> T getModel(Class<T> clazz) {
                return clazz.cast(value);
            }
        };
        B b = test.getModel(B.class);
    }
}
 

White_Fox

Top Contributor
Ich glaub ich verstehe...einmal <T extends SomeClass> reicht. Ist das richtig so? Der Compiler weiß dann für die gesamte Methode, daß T SomeClass erweitert?

Und noch eine Frage: Wie kriege ich ein ArrayList<T extends SomeClass> zurückgeliefert? Nach etwas Schmökerei in den Oracle Tutorials hab ich das hier mal ausprobiert:
Java:
abstract public ArrayList<? extends TreeItemModel> getModel(Class<? extends TreeItemModel> modelClass);
Aber was macht das Fragezeichen da jetzt genau? Ohne Fragezeichen, also irgendeinem Bezeichner, meckert der Compiler, mit Fragezeichen aber nicht.

So wie ich die Doku bisher verstanden habe ist das Fragezeichen sozusagen ein Platzhalter für irgendwas-aber dann verstehe ich nicht wieso das mit konkretem Bezeichner nicht geht.
 

mrBrown

Super-Moderator
Mitarbeiter
Das <T externes XY> steht vor dem Rückgabetyp, damit legst du T für diese Methode fest.

Überall in der restlichen Methode nutzt du dann nur noch T, ohne zu spezifizieren, was T ist.

Das Fragezeichen ist eine Wildcard, einfach ausgedrückt „irgendwas“.


Die ArrayList als Rückgabetyp kannst du so angeben: <T extends SomeClass> ArrayList<T>
 

mihe7

Top Contributor
Im Prinzip ist es ganz einfach:

Es gibt generische Klassen und generische Methoden. Bei der Klasse wird der Typparameter nach dem Klassennamen angegeben: class Klasse<T extends SomeClass>, bei der Methode dagegen vor dem Rückgabewert: <T extends SomeClass> void doit(List<T> listOfSomeClass)

Wildcards sind etwas schwieriger zu verstehen. Während <T> vom Compiler als "eine gegebene Klasse T" behandelt wird, ist <?> für den Compiler "ein nicht näher bekannter Typ".

Interessant sind Wildcards im Zusammenhang mit Polymorphie. Dazu muss ich etwas ausholen.

Nehmen wir mal an, Du hast einen Typ T und einen Subtyp S (S extends T). Außerdem eine generische Klasse K. In Code also etwas wie

Java:
class T {}
class S extends T {}
class K<V> {
    V value;
    public void set(V value) { this.value = value; }
    public V get() { return value; }
}

Dann könnte man auf die Idee kommen, dass K<S> ein Subtyp von K<T>, beispielsweise eine List<Double> Subtyp einer List<Number> wäre. Das ist aber nicht der Fall.

D. h. eine Variable vom Typ K<T> kann keine Instanz von K<S> referenzieren, weil ansonsten könnte man der Instanz von K<S> ein T unterjubeln:
Java:
List<Number> numbers = new ArrayList<>();
List<Double> doubles = numbers; // Fehler, weil sonst
numbers.add(new BigDecimal(5)); // könnte man über numbers 
Double value = doubles.get(0); // den doubles ein BigDecimal für ein Double vormachen.

Das hat zur Folge, dass die Behandlung zum Beispiel in Methodenparametern ggf. unschön wird. So kann die Methode
Java:
public double add(List<Number> numbers) {
    double sum = 0;
    for (Number nr : numbers) { sum += nr.doubleValue(); }
    return sum;
}
nur mit einer List<Number>, nicht aber z. B. mit einer List<Double> aufgerufen werden.

Tatsächlich ist es aber genau das, was man doch eigentlich möchte: eine Liste verwenden, deren Typparameter irgendeine von Number abgeleitete Klasse ist. Und jetzt kommen die Wildcards ins Spiel, "irgendeine von Number abgleitete Klasse" ist nichts anderes als <? extends Number>, also
Java:
public double add(List<? extends Number> numbers) {
    double sum = 0;
    for (Number nr : numbers) { sum += nr.doubleValue(); }
    return sum;
}
Die Methode lässt sich nun auch mit einer List<Double> aufrufen.
 

White_Fox

Top Contributor
Ah...ja, ich glaub das hab ich verstanden, vielen Dank.

Wenn ich jetzt folgende Methodendeklaration habe:
Java:
abstract protected <T> T getItemModel(Class<? extends TreeItemModel> modelClass);
Entspricht der Typ T dann dem, den ich mit dem Parameter modelClass übergeben habe? Irgendwie sehe ich nicht, wie der Compiler vom Methodenparameter auf den Rückgabetyp schließt, aber da meckert er immerhin nicht. Ich könnte ja z.B. noch einen zweiten Typ U irgendwo haben, wie würde ich denn den dann deklarieren, wenn ich zwei Wildcards habe?

Java:
public <T> T methodName(<T extends AClass> t, <U extends AnOtherClass> u)
 

mihe7

Top Contributor
Entspricht der Typ T dann dem, den ich mit dem Parameter modelClass übergeben habe?
Nein.
Irgendwie sehe ich nicht, wie der Compiler vom Methodenparameter auf den Rückgabetyp schließt
Gar nicht.
aber da meckert er immerhin nicht.
Versuch das mal, zu implementieren :) Mehr als ein null wirst Du nicht zurückgeben können.

ich könnte ja z.B. noch einen zweiten Typ U irgendwo haben, wie würde ich denn den dann deklarieren, wenn ich zwei Wildcards habe?

Java:
public <T extends AClass, U extends AnOtherClass> T methodName(T t, U u)
Eine Wildcard "bräuchtest" Du ja nur, wenn Du bei den Parametern einen generischen Typ verwendest, z. B. eine List<AnOtherClass>, dann hättest Du
Java:
public <T extends AClass> T methodName(T t, List<? extends AnOtherClass> u)
 

White_Fox

Top Contributor
Würde daß dann so funktionieren?
Java:
abstract protected <T extends TreeItemModel> T getItemModel(Class<? extends TreeItemModel> modelClass);
Der Rückgabewert kann vom Typ <? extends TreeItemModel> sein, aber auch von einem daraus abgeleiteten. So, wie ich dich verstanden habe, müßte der Rückgabetyp dann <? extends <? extends TreeItemModel>> sein. Aber der Compiler haut mir alles mit mehr als einr Wildcard um die Ohren.
 
Zuletzt bearbeitet:

mrBrown

Super-Moderator
Mitarbeiter
Kannst du mal ein konkretes Beispiel für T, <? extends TreeItemModel> und <? extends <? extends TreeItemModel>> geben?
 

White_Fox

Top Contributor
Hm...Code habe ich noch nicht, ich bin aber schon dabei, einen Unittest zu screiben, den stell ich nachher rein.
Aber zum Erklären: ich hatte weiter oben ja die Metapher mit den Büchern in Regalen, Regalen in den Häusern, und Häusern in Städten.

Angenommen mich interessieren jetzt weniger die Bücher, sondern nur das, was Bücher direkt oder indirekt enthält.
Dann würde ich jetzt eine Klasse "BuchContainer" schreiben und diese von TreeItemModel ableiten.
Von dieser Klasse würde ich jetzt die beiden Klassen "BuchContainerRegal" und "BuchContainerHaus" ableiten.

Jetzt möchte ich die Methode mit BuchContainer.getClass() füttern, und die Methode soll-je nach Implementierung, die ist Haus-Klasse und der Regal-Klasse unterschiedlich-ein BuchContainerRegal-Objekt oder ein BuchContainerHaus-Objekt zurückliefern können.
 

mihe7

Top Contributor
Der Rückgabewert kann vom Typ <? extends TreeItemModel> sein, aber auch von einem daraus abgeleiteten. So, wie ich dich verstanden habe, müßte der Rückgabetyp dann <? extends <? extends TreeItemModel>> sein.
Nein.
Java:
T getItemModel(...);
besagt, dass getItemModel eine Referenz auf ein Objekt vom Typ T liefert. Hier fehlt aber noch die Deklaration von T. Die steht vor dem Rückgabetyp:
Java:
<T extends TreeItemModel> T getItemModel(...);
besagt, dass getItemModel eine Referenz auf ein Objekt vom Typ T liefert, wobei T ein Subtyp von TreeItemModel sein muss (<T extends TreeItemModel>). Was Du getItemModel als Parameter spendierst, ändert daran nichts.

Jetzt möchte ich die Methode mit BuchContainer.getClass() füttern, und die Methode soll-je nach Implementierung, die ist Haus-Klasse und der Regal-Klasse unterschiedlich-ein BuchContainerRegal-Objekt oder ein BuchContainerHaus-Objekt zurückliefern können.
Meinst Du sowas in der Richtung?
Java:
import java.util.ArrayList;
import java.util.List;

public class Test {
    static class Node<T> {
        private T value;
        private List<Node<T>> children = new ArrayList<>();

        public Node(T value) { this.value = value; }

        public void add(Node<T> child) {
            children.add(child);
        }

        public <V> V findFirst(Class<V> clazz) {
            if (value != null && clazz.isInstance(value)) {
                return clazz.cast(value);
            }

            for (Node<T> child : children) {
                return child.findFirst(clazz);
            }

            return null;
        }
    }

    interface Item {}
    static class Regal implements Item {}
    static class Buch implements Item {}
     

    public static void main(String[] args) {
        Regal r = new Regal();
        Buch b = new Buch();
        Node<Item> root = new Node<>(r);
        root.add(new Node<>(b));

        System.out.println(root.findFirst(Test.class));
        System.out.println(root.findFirst(Regal.class));
        System.out.println(root.findFirst(Buch.class));
    }
}
 

White_Fox

Top Contributor
Hm...nein, das ist es noch nicht so ganz, glaube ich. Ich probiere es mal so:
Java:
//Vererbungsbaum:
public abstract class TreeItemModel{}

public class PoorlyModel extends TreeItemModel{}

public class ExtendedModel extends TreeItemModel{}

public class MoreExtendedModelA extends ExtendedModel{}
public class MoreExtendedModelB extends ExtendedModel{}

//Implementierung der Methode:
abstract public class TreeItem{
    //...
    abstract <? extends M> T getItemModel(Class<? extends TreeItemModel> M ); //Murks, aber vielleicht wird das so deutlicher wie ich das meine
}

Beispiel: Wenn ich die Methode getItemModel mit einem ExtendedModel.getClass füttere, dann kann sie ein ExtendedModel zurückgeben, es kann aber auch ein MoreExtendedModelA oder ein MoreExtendedModelB sein.
Die Methode wird jedoch z.B. kein PoorlyModel, das ja auch ein TreeItemModel ist, zurückgeben oder eine andere Klasse, die im Vererbungsbaum über oder neben ExtendedModel liegt.

Edit:
Quatsch...Ich füttere die Methode ja mit einem Class-Objekt, TreeItemModel ist ja was ganz anderes.
 
Zuletzt bearbeitet:

White_Fox

Top Contributor
Das hier haut mir der Compiler nicht mehr um die Ohren:
Java:
abstract <T extends TreeItemModel> T getItemModel(Class<? extends TreeItemModel> M);
Jetzt kann ich allerdings nicht mehr sicherstellen das die Methode nicht irgendein TreeItemModel zurückgibt. Mal sehen, vielleicht ist das aber auch gar nicht nötig. Ich denke, ich werde damit mal einen Test versuchen.
 

mihe7

Top Contributor
Wenn ich die Methode getItemModel mit einem ExtendedModel.getClass füttere, dann kann sie ein ExtendedModel zurückgeben, es kann aber auch ein MoreExtendedModelA oder ein MoreExtendedModelB sein.
Ja, genau das ist bei mir auch der Fall. Mein Node ist Dein TreeItem und mein Item, Regal und Buch sind Deine TreeItemModel, PoorlyModel etc.

Die Methode wird jedoch z.B. kein PoorlyModel, das ja auch ein TreeItemModel ist, zurückgeben oder eine andere Klasse, die im Vererbungsbaum über oder neben ExtendedModel liegt.
Das geht doch immer (is-a-Beziehung)

Jetzt kann ich allerdings nicht mehr sicherstellen das die Methode nicht irgendein TreeItemModel zurückgibt. Mal sehen, vielleicht ist das aber auch gar nicht nötig.

Was hast Du gegen
Java:
<T extends TreeItemModel> T getItemModel(Class<T> M)
?
 

White_Fox

Top Contributor
Was hat denn eigentlich die ArrayList für ein Problem mit meinen Generics?

Java:
public ArrayList<? extends TreeItemModel> getTreeModel(Class<? extends TreeItemModel> m){
    ArrayList<? extends TreeItemModel> list = new ArrayList<>();
    ArrayList<? extends TreeItemModel> tmpList;
    //...
    list.addAll(tmpList); // Da meckert er: no suitable method found for addAll ...

Ich vermute daß ich meine ArrayList list anders deklarieren muß...nur wie? Von welchem Typ die ArrayList sein soll steht ja im Parameter m, aber m ist ja erstmal vom Typ Class. Kann ich das irgendwie in Generics verwursten?

Nachtrag:
Vielleicht hab ich es auch alleine hinbekommen:
Java:
public <T extends TreeItemModel> ArrayList<T> getTreeModel(Class<T> m){
        ArrayList<T> list = new ArrayList<>();
        ArrayList<T> tmpList;
        //...
        list.addAll(tmpList);
Passt das, oder macht es was anderes als ich da will?
 
Zuletzt bearbeitet:

White_Fox

Top Contributor
So...jetzt scheint das Ganze zu funktionieren. Ich werde das ganze Gebilde in eine eigene Bibliothek auslagern, das kann ich jetzt auch anderso verwenden.

Vielen Dank für eure Hilfe @mihe7 und @mrBrown. Ich hab-als kleine Revange-den Code und den Test als Beispiel in den Spoiler gepackt. Wer es verwenden kann und will-bitteschön, ist hiermit zur freien Verwendung freigegeben.

Die Klasse Address:
Java:
import java.util.ArrayList;
import java.util.UUID;

/**
 * This class helps to identify a library object which implements
 * the Addressable-interface. It marks a path straight through the
 * tree of Addressables and their child-objects.<p>
 *
 * The navigation works with a stack if ids, and every id marks
 * an item in the library tree. First in stack marks tree root,
 * the next marks the according child from the tree, the next
 * the child from first child, ...
 * So this class provides a way to walk through the tree to a
 * specified item in library. This way starts at the tree root and
 * ends at the destination item.
 *
 * @author White_Fox from java-forum.org
 */
public class Address {
    
    private ArrayList<UUID> idPath;

    Address(UUID rootID) {
        this.idPath = new ArrayList<>();
        this.idPath.add(rootID);
    }

    /**
     * Adds the unique id from an TreeItem to the stack. The last
     * added item marks the addressed item, its parent has the
     * next-to-last added item, ...<p>
     * The method checks the resultig path for correctnes, including
     * that there is no doubled item. If the resulting tree path
     * would be uncorrect, the method returns false and will not
     * add the item to tree path.<p>
     * If the method returns true, adding the item UUID was successfull.
     *
     * @param item last item-UUID in queue.
     * @return true, if adding UUID to tree path was successfull,
     * false otherwise.
     */
    boolean addItemToEndOfPath(UUID item){
        if(idPath.contains(item)){
            return false;
        }
        else{
            idPath.add(item);
            return true;
        }
    }
    
    /**
     * Returns true, if the UUID in parameter is part of the tree path.
     * Fals otherwise.
     *
     * @param item the UUID to test.
     * @return true, if item is in path.
     */
    boolean IsInPath(UUID item){
        return idPath.contains(item)? true : false;
    }
    
    /**
     * Feed this method with an UUID, and it returns the UUID from
     * the next following child in tree, in direction from root
     * to destination. If there is no child, including cause the
     * param equals the destination or the UUID in parameter is not
     * part of the path, the method returns null.
     *
     * @param item is the parents UUID.
     * @return the UUID from child.
     */
    UUID getNext(UUID item){
        int i = 0;
        while (i < idPath.size() && !idPath.get(i).equals(item)) {           
            i++;
        }
        
        if(i >= idPath.size() - 1){return null;}
        
        return idPath.get(i + 1);
    }
    
    /**
     * Feed this method with an UUID, and it returns the UUID from
     * the parent in tree, in direction from root
     * to destination. If there is no parent, including cause the
     * param equals the destination or the UUID in parameter is not
     * part of the path, the method returns null.
     *
     * @param item
     * @return
     */
    UUID getBack(UUID item){
        int i = 0;
        while (i < idPath.size() && !idPath.get(i).equals(item)) {           
            i++;
        }
        
        if(i >= idPath.size() || i == 0){return null;}
        
        return idPath.get(i - 1);
        
    }
    
    /**
     * This method returns the last UUID in the path. In other words,
     * that means the UUID from the addressed tree item.
     *
     * @return the UUID from addressed tree item.
     */
    UUID getLast(){
        return idPath.get(idPath.size() - 1);
    }
    
    ArrayList<UUID> getIdPath(){
        return idPath;
    }
    
    /**
     * This method compares an Address-object with an other. It false
     * in every case, if o is null or o is not an Address-object.<p>
     * The method returns also false, if both objects describes
     * different ways through the tree. It returns only and only true
     * in the case, that both methods describes the same path through
     * the tree, that means the same start and the same destination
     * with the same TreeItems in the same order.
     *
     * @param o is an object to compare.
     * @return true that both are Address-objects and describes the
     * same way, false otherwise.
     */
    @Override
    public boolean equals(Object o){
        if(o == null || !o.getClass().equals(this.getClass())){
            return false;
        }
        
        Address a = (Address)o;
        return a.getIdPath().equals(idPath);
    }
}

Die Klasse TreeItemModel:
Java:
/**
 * This class needs to be inherited and provides getting information
 * out of the tree, without returning all complete tree items.<p>
 * Do as the following:<br>
 * Inherite one or more classes from TreeItemModel, according to
 * a specific subclass of {@link TreeItem} and application requirements.
 * Override the getItemModel()-method of the TreeItem-class in a way
 * to fill this class with all data which are needed. Thats all.
 *
 * @author White_Fox from java-forum.org
 */
abstract public class TreeItemModel {

    private final Address a;
    private TreeItemModel parent;
    
    public TreeItemModel(Address a) {
        this.a = a;
        this.parent = null;
    }
    
    /**
     * Sets the parent TreeItemModel. Only useful for generating
     * this object, so its protected.
     *
     * @param t is the parent TreeItemModel for this instance.
     */
    protected void setParent(TreeItemModel t){
        this.parent = t;
    }
    
    /**
     * This method returns the parent of this TreeItemModel. A parent
     * does not in all cases exist, maybe its root of tree or the
     * model is implemented in a way, that there is no parent.<p>
     *
     * In all cases that a parent doesn't exist or is faded out, the
     * method returns null.
     *
     * @return the parent TreeItemModel.
     */
    public TreeItemModel getParent(){
        return this.parent;
    }
    
    /**
     * This method returns an Address-object to access the according
     * {@link TreeItem} in the tree. An Address-object can be invalid,
     * e.g. the tree has changed after generating the model. In this
     * case, you should generate a new model.
     *
     * @return the Address-object from acording TreeItem in tree.
     */
    public Address getAddress(){
        return this.a;
    }
}

Die Klasse TreeItem:
Java:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * This class represents a TreeItem and provides navigation through
 * the library tree.
 *
 * @author White_Fox from java-forum.org
 */
public abstract class TreeItem {

    private HashMap<UUID, TreeItem> children;
    private final UUID ME;
    private TreeItem parent;

    /**
     * This returns a new TreeItem-object which act as tree root. That
     * causes e.g. that an Address-request from a child stops at this
     * instance.
     */
    public TreeItem() {
        this.children = new HashMap<>();
        this.ME = UUID.randomUUID();
        this.parent = null;
    }

    /**
     * This returns a new TreeItem-object. Its neccessary to announce
     * a TreeItem instance as parent. For the case that parent is
     * null, the instance acts as tree root node. That causes e.g.
     * that an Address-request from a child stops at this instance.
     *
     * @param parent is the parent item in tree
     */
    public TreeItem(TreeItem parent) {
        this.children = new HashMap<>();
        this.ME = UUID.randomUUID();
        this.parent = parent;
        parent.addChild(this, parent.getAddress());
    }

    /**
     * This method returns true, if the Addres-object points this
     * instance directly. That means, that the ID of this instance is
     * the last in ID-stack of the Address-object.<p>
     *
     * In any other case, including that this instance is anywhere or
     * not in address path, the method returns false.
     *
     * @param a is the Address-object to test.
     * @return true, if the Address-object points this instance.
     */
    private boolean itMeansMe(Address a) {
        return ME.equals(a.getLast());
    }

    /**
     * This method returns true, if this instance is the parent from
     * the addressed tree item. False otherwiese.
     *
     * @param is an Address-object.
     * @return true, if this instance is the parent from the addressed
     * object.
     */
    private boolean iAmTheParent(Address a) {
        UUID parent;

        parent = a.getBack(a.getLast());
        return parent.equals(ME);
    }

    /**
     * Set the value of parent.
     *
     * @param parent new value of parent.
     */
    void setParent(TreeItem parent) {
        this.parent = parent;
    }

    /**
     * Returns the unique id of this instance as UUID-object.
     *
     * @return the unique id.
     */
    UUID getID() {
        return this.ME;
    }

    /**
     * Returns the parent of the TreeItem-object. In case that there
     * is no parent, the method returns null.
     *
     * @return the parent TreeItem of this object.
     */
    TreeItem getParent() {
        return this.parent;
    }

    /**
     * With this method, the object can be informed that its parent
     * changed.
     *
     * @param newParent is the new parent TreeItem.
     */
    void changeParent(TreeItem newParent) {
        this.parent = newParent;
    }

    /**
     * This method returns the actual address of the object in the
     * item tree.
     *
     * @return the address of the tree item.
     */
    Address getAddress() {
        Address a;

        if (this.parent == null) {
            a = new Address(ME);
        }
        else {
            a = this.parent.getAddress();
            a.addItemToEndOfPath(ME);
        }
        return a;
    }

    /**
     * This method returns the child, which is direct addressed by the
     * Address-object or which is the next item in the destination
     * path of the Address-object.<p>
     * In Case that there is no child according to the
     * Address-Parameter, the method returns null.
     *
     * @param a is the Address-object guiding through the tree
     * @return the child which lies in the Address-path
     */
    TreeItem getNext(Address a) {
        //I am part of address path?
        if (!a.IsInPath(ME)) {
            return null;
        }

        if (itMeansMe(a)) {
            return this;
        }
        else {
            return children.containsKey(a.getNext(ME))
                    ? children.get(a.getNext(ME))
                    : null;
        }
    }

    /**
     * This method adds a tree item to the tree, the parent is
     * specified by an Address-object. It returns false, if the
     * Address to parent is faulty, if the item is already added to
     * parent, or if adding goes wrong in an other way. It returns
     * true if adding was successfull.
     *
     * @param item is the new tree item.
     * @param parent is the parent of the new item.
     * @return true, if adding was successfull, false otherwise.
     */
    public boolean addChild(TreeItem item, Address parent) {
        //Check that this item is part of tree path.
        if (!parent.IsInPath(ME)) {
            return false;
        }

        //Check that item is not doubbled.
        if (children.containsKey(item.getID())) {
            return false;
        }

        if (itMeansMe(parent)) {
            children.put(item.getID(), item);
            item.changeParent(this);
        }
        else {
            children.get(parent.getNext(ME)).addChild(item, parent);
        }
        return true;
    }

    /**
     * This method removes a tree item from the tree. It needs an
     * Address-object which points the tree item to remove. If
     * removing fails in any case, e.g. faulty addressing, the method
     * returns null.
     *
     * @param item is the address of item to remove.
     * @return the removed item.
     */
    public TreeItem removeChild(Address item) {
        //Check that this item is part of tree path.
        if (!item.IsInPath(ME)) {
            return null;
        }
        
        //Check that addressed child exist
        if(!children.containsKey(item.getNext(ME)) && !itMeansMe(item)){
            return null;
        }

        if (iAmTheParent(item)) {
            return children.remove(item.getNext(ME));
        }
        else {
            return children.get(item.getNext(ME)).removeChild(item);
        }
    }

    /**
     * This method returns a tree child. The tree child is defined by
     * an Address-object. If the child couldn't be found, the mehtod
     * returns null.
     *
     * @param a Address-object of returned child-element.
     * @return a child-element.
     */
    public TreeItem getChild(Address a) {
        //Check that this item is part of tree path.
        if (!a.IsInPath(ME)) {
            return null;
        }
        
        //Check that addressed child exist
        if(!children.containsKey(a.getNext(ME)) && !itMeansMe(a)){
            return null;
        }

        if (itMeansMe(a)) {
            return this;
        }
        else {
            return children.get(a.getNext(ME)).getChild(a);
        }
    }
    
    /**
     * This method returns a class-dependent model of this TreeItem.
     * Use this to export special information from this object,
     * according to modelClass. See {@link TreeItemModel} for further
     * information.<p>
     * If M suits not to this class, the method returns null.
     *
     * @param <T> is the type of TreeItemModel.
     * @param m specifies the kind of TreeItemModel.
     * @return the according TreeItemModel of this instance.
     */
    abstract <T extends TreeItemModel> T getItemModel(Class<T> m);
    
    /**
     * This method returns an ArrayList with inheritated objects from
     * TreeItemModels. The ArrayList contains returned
     * TreeItemModel-objects of all children, basing of parameter
     * m.
     *
     * @param m specifies the kind of TreeItemModel.
     * @return an Array which contains all according TreeItemModels of
     * all children elements.
     */
    public <T extends TreeItemModel> ArrayList<T> getTreeModel(Class<T> m){
        ArrayList<T> list = new ArrayList<>();
        ArrayList<T> tmpList;
        TreeItemModel thisModel = getItemModel(m);
        Address a = getAddress();
        
        for(Map.Entry<UUID, TreeItem> entry : children.entrySet()){
            tmpList = entry.getValue().getTreeModel(m);
            if(tmpList.size() > 0){
                if(tmpList.get(tmpList.size() - 1) != null){
                    if(thisModel != null){
                        tmpList.get(tmpList.size() - 1).setParent(thisModel);
                    }
                }
                else{
                    tmpList.remove(tmpList.size() - 1);
                }
                list.addAll(tmpList);
            }
        }
        
        if(parent != null){
            list.add(thisModel == null ? null : (T)thisModel);
        }
        return list;
    }
}

Unittest:
Java:
import java.util.ArrayList;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 *
 * @author White_Fox from java-forum.org
 */
public class TreeItemTest {
    
    class TestTreeItem extends TreeItem{
        
        public TestTreeItem(TreeItem parent) {
            super(parent);
        }
        
        public TestTreeItem() {
            super();
        }

        @Override
        <T extends TreeItemModel> T getItemModel(Class<T> M) {
            return null;
        }
    }
    
    class TreeInt extends TreeItem{

        int i;
        
        public TreeInt(TreeItem parent, int i) {
            super(parent);
            this.i = i;
        }

        @Override
        <T extends TreeItemModel> T getItemModel(Class<T> M){
            TreeItemModel model;
            
            if(M.equals(IntModel.class)){
                model = new IntModel(super.getAddress(), i);
                return M.cast(model);
            }
            
            if(M.equals(StringModel.class)){
                model = new StringModel(super.getAddress(), String.valueOf(i));
                return M.cast(model);
            }
            
            return null;
        }
        
    }
    
    class TreeString extends TreeItem{

        String s;
        
        public TreeString(TreeItem parent, String s) {
            super(parent);
            this.s = s;
        }

        @Override
        <T extends TreeItemModel> T getItemModel(Class<T> M) {
            
            if(M.equals(StringModel.class)){
                StringModel model;
                model = new StringModel(super.getAddress(), s);
                return M.cast(model);
            }
            
            return null;
        }
    }
    
    class IntModel extends TreeItemModel{
        
        private int integer;

        public IntModel(Address a, int i) {
            super(a);
            this.integer = i;
        }

        int getInteger() {
            return this.integer;
        }
    }
    
    class StringModel extends TreeItemModel{
        String string;
        
        public StringModel(Address a, String s) {
            super(a);
            this.string = s;
        }
        
        public String getString(){
            return string;
        }
    }
    
    public TreeItemTest() {
    }

    TreeItem root;
    TreeItem item1_1, item1_2, item1_3;
    TreeItem item2_1, item2_2, item2_3;
    TreeItem item3_1, item3_2, item3_3;

    private void buildTree(){
        Address a;
        
        root = new TestTreeItem();
        
        item1_1 = new TestTreeItem();
        item1_2 = new TestTreeItem();
        item1_3 = new TestTreeItem();
        a = root.getAddress();
        root.addChild(item1_1, a);
        root.addChild(item1_2, a);
        root.addChild(item1_3, a);
        
        item2_1 = new TestTreeItem();
        item2_2 = new TestTreeItem();
        item2_3 = new TestTreeItem();
        a = item1_2.getAddress();
        root.addChild(item2_1, a);
        root.addChild(item2_2, a);
        root.addChild(item2_3, a);
        
        item3_1 = new TestTreeItem();
        item3_2 = new TestTreeItem();
        item3_3 = new TestTreeItem();
        a = item2_2.getAddress();
        root.addChild(item3_1, a);
        root.addChild(item3_2, a);
        root.addChild(item3_3, a);
    }
    
    //Make a tree like this:
    //       root
    //         |
    // int--String--int
    //         |
    // int--String--int
    //         |
    // int--String--int
    //         |
    private void buildModelTree(){
        
        root = new TestTreeItem();
        
        item1_2 = new TreeString(root, "Item1_2");
        item1_1 = new TreeInt(item1_2, 11);
        item1_3 = new TreeInt(item1_2, 12);

        item2_2 = new TreeString(item1_2, "Item2_2");
        item2_1 = new TreeInt(item2_2, 21);
        item2_3 = new TreeInt(item2_2, 23);
        
        item3_2 = new TreeString(item1_2, "Item3_2");
        item3_1 = new TreeInt(item3_2, 31);
        item3_3 = new TreeInt(item3_2, 33);
    }

    /**
     * Test of setParent method, of class TreeItem.
     */
    @Test
    public void testSetParent() {
        TreeItem instance;
        TreeItem result, expResult;
        
        System.out.println("setParent");
        
        buildTree();
        instance = new TestTreeItem(root);

        instance.setParent(item3_2);
        expResult = item3_2;
        result = instance.getParent();
        
        assertEquals(expResult, result);
    }

    /**
     * Test of getParent method, of class TreeItem.
     */
    @Test
    public void testGetParent() {
        TreeItem instance;
        TreeItem result, expResult;
        
        System.out.println("getParent");
        
        buildTree();
        instance = new TestTreeItem(item2_2);
        expResult = item2_2;
        result = instance.getParent();
        
        assertEquals(expResult, result);
    }

    /**
     * Test of changeParent method, of class TreeItem.
     */
    @Test
    public void testChangeParent() {
        TreeItem instance;
        TreeItem result, expResult;
        
        System.out.println("changeParent");
        
        buildTree();
        instance = new TestTreeItem(root);
        
        expResult = item2_2;
        instance.changeParent(expResult);
        result = instance.getParent();
        
        assertEquals(expResult, result);
    }

    /**
     * Test of getNext method, of class TreeItem.
     */
    @Test
    public void testGetNext() {
        TreeItem instance;
        Address a;
        TreeItem result, expResult;
        
        System.out.println("getNext");
        
        buildTree();
        instance = item2_2;
        
        a = item3_2.getAddress();
        expResult = item3_2;
        result = instance.getNext(a);
        assertEquals(expResult, result);
        
        a = item1_3.getAddress();
        expResult = null;
        result = instance.getNext(a);
        assertEquals(expResult, result);
    }

    /**
     * Test of addChild method, of class TreeItem.
     */
    @Test
    public void testAddChild() {
        TestTreeItem instance;
        Address a;
        boolean result, expResult;
        
        System.out.println("addChild");
        
        buildTree();
        //Add instance to the tree
        instance = new TestTreeItem();
        
        a = item3_2.getAddress();
        result = root.addChild(instance, a);
        expResult = true;
        assertEquals(expResult, result);
        
        buildTree();
        result = root.addChild(instance, a);
        expResult = false;
        assertEquals(expResult, result);
    }

    /**
     * Test of removeChild method, of class TreeItem.
     */
    @Test
    public void testRemoveChild() {
        Address a;
        TreeItem result, expResult;
        
        System.out.println("removeChild");
        
        buildTree();
        a = item3_2.getAddress();
        
        expResult = item3_2;
        result = root.removeChild(a);
        assertEquals(expResult, result);
        
        buildTree();
        expResult = null;
        result = root.removeChild(a);
        assertEquals(expResult, result);
    }

    /**
     * Test of getChild method, of class TreeItem.
     */
    @Test
    public void testGetChild() {
        Address a;
        TreeItem result, expResult;
        
        System.out.println("getChild");
        
        buildTree();
        a = item3_2.getAddress();
        
        expResult = item3_2;
        result = root.getChild(a);
        assertEquals(expResult, result);
        
        buildTree();
        expResult = null;
        result = root.removeChild(a);
        assertEquals(expResult, result);
    }

//    /**
//     * Test of getAddress method, of class TreeItem.
//     */
//    @Test
//    public void testGetAddress() {
//        TestTreeItem instance;
//        Address result, expResult;
//       
//        System.out.println("getAddress");
//        //No idea how to test
//    }

    /**
     * Test of getItemModel method, of class TreeItem.
     */
    @Test
    public void testGetItemModel() {
        TreeItem instance;
        TreeItemModel model;
        Object result, expResult;
        
        System.out.println("getItemModel");
        
        buildModelTree();
        instance = item2_1;
        
        model = instance.getItemModel(IntModel.class);
        expResult = IntModel.class;
        result = model.getClass();
        assertEquals(expResult, result);
        
        model = item2_1.getItemModel(StringModel.class);
        expResult = StringModel.class;
        result = model.getClass();
        assertEquals(expResult, result);
    }

    /**
     * Test of getTreeModel method, of class TreeItem.
     */
    @Test
    public void testGetTreeModel() {
        TreeItem instance;
        Object result, expResult;
        ArrayList<? extends TreeItemModel> model;
        
        System.out.println("getTreeModel");
        
        buildModelTree();
        instance = root;
        
        //Test with Int - only some tree elements returns a model
        //Test amount of results
        model = instance.getTreeModel(IntModel.class);
        expResult = 6; //There are six Items which made an IntModel in tree.
        result = model.size();
        assertEquals(expResult, result);
        
        //Test kind of model items
        for(TreeItemModel itemmodel : model){
            expResult = IntModel.class;
            result = itemmodel.getClass();
            assertEquals(expResult, result);
        }
        
        //Test link between tree item and its model
        for(TreeItemModel itemmodel : model){
            IntModel m = (IntModel)itemmodel;
            if(m.getInteger() == 23){
                expResult = null;
                result = m.getParent();
                assertEquals(expResult, result);
                
                expResult = item2_3.getAddress();
                result = m.getAddress();
                assertEquals(expResult, result);
                
                expResult = null;
                result = m.getParent();
                assertEquals(expResult, result);
            }
        }
        
        
        //Test with String - all elements except the root returns a model
        //Test amount of results
        model = instance.getTreeModel(StringModel.class);
        expResult = 9; //There are nine Items which made an StringModel in tree.
        result = model.size();
        assertEquals(expResult, result);
        
        //Test kind of model items
        for(TreeItemModel itemmodel : model){
            expResult = StringModel.class;
            result = itemmodel.getClass();
            assertEquals(expResult, result);
        }
        
        //Test link between tree item and its model
        for(TreeItemModel itemmodel : model){
            StringModel m = (StringModel)itemmodel;
            if(m.getString().equals("23")){
                for(TreeItemModel tmp : model){
                    StringModel md = (StringModel)tmp;
                    if(md.getString().equals("Item2_2")){expResult = tmp;}
                }
                result = m.getParent();
                assertEquals(expResult, result);
                
                expResult = item2_3.getAddress();
                result = m.getAddress();
                assertEquals(expResult, result);
            }
            
            if(m.getString().equals("Item1_2")){
                expResult = null;
                result = m.getParent();
                assertEquals(expResult, result);
            }
        }
    }
}
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
P Generische Typen und Comparable Softwareentwicklung 6
MQue JNI Datentypen Softwareentwicklung 3

Ähnliche Java Themen

Neue Themen


Oben