Klasse erweitern

Herr Kaiser

Bekanntes Mitglied
Hallo Forum,

ich habe eine Anwendung geschrieben, mit der ich inzwischen zahlreiche Objekte erstellt habe, die ich durch Serialisierung auf der Festplatte gespeichert habe. Nun ist die Notwendigkeit entstanden, allen Objekten eine zusätzliche Eigenschaft hinzuzufügen.

Wenn ich allerdings die zugrundeliegende Klasse anpasse, lassen sich die gespeicherten Objekte nicht mehr deserialisieren, da die Klasse nicht mehr zu den Objekten passt. Meine Objekte haben einen recht abstrakten Inhalt. Deshalb versuche ich mal ein Analogbeispiel, um das Problem zu veranschaulichen:

Angenommen, ich hätte zahlreiche Objekte der Klasse "Haus" gespeichert und möchte diesen die Boolsche Variable "feuermelderInstalliert" hinzufügen.

Ich dachte ich mache sowas wie:

Java:
public class ErweitertesHaus extends Haus {

    Boolean feuermelderInstalliert;

    ErweitertesHaus() {
            feuermelderInstalliert=false;
    }
}


Meine Vorstellung war, dass ich dann mit der Klasse Haus meine vorhandenen Objekte (z.B. meinHaus) deserialisiere und diese dann irgendwie nach ErweitertesHaus caste:


Java:
ErweitertesHaus meinErweitertesHaus = new(ErweitertesHaus);
meinErweitertesHaus = (ErweitertesHaus) meinHaus;

Aber scheinbar habe ich die Vererbungslehre noch nicht richtig begriffen, oder es fehlen noch wichtige Schritte, denn der Ansatz funktioniert so nicht (Haus cannot be cast to ErweitertesHaus).

Hättet Ihr daher einen kleinen Denkanstoß für mich, wie ich auf dem angedachten oder vielleicht auch auf einem ganz anderen Wege das Problem lösen könnte? Das wäre ziemlich klasse! ...
 
Zuletzt bearbeitet:
Bei Vererbung kannst du immer ein Objekt eines konkreteren Typs (hier ErweitertesHaus) einer Variablen des generalisierten Typs zuweisen.

Dein Beispielcode wäre umgekehrt korrekter:

Java:
Haus meinHaus = new ErweitertesHaus();

Dein Code mit casten klappt nicht, da du das normale "Haus" dem Typen "ErweitertesHaus" zuweisen möchtest und das "Haus" nicht den "feuermelderInstalliert" kennt. Obiger Code klappt, da "ErweitertesHaus" alle Eigenschaften von "Haus" besitzt. Man kann nur nicht auf den Feuermelder zugreifen. Dann müsstest du casten:

Java:
Haus meinHaus = new ErweitertesHaus();
ErweitertesHaus meinErweitertesHaus = (ErweitertesHaus ) meinHaus ;
 
Hallo Herr Kaiser,

du könntest z.B. einen Konstruktor definieren,
Java:
public ErweitertesHaus(Haus haus) { ...
so dass du auf der Grundlage eines Haus-Objekts ein neues Objekt (nämlich ein ErweitertesHaus) erstellst.
 
du könntest z.B. einen Konstruktor definieren,
Java:
public ErweitertesHaus(Haus haus) { ...
so dass du auf der Grundlage eines Haus-Objekts ein neues Objekt (nämlich ein ErweitertesHaus) erstellst
Genau.
Das Zauberwort heißt hier Composition over Inheritance (CoI).
Dein neues
Code:
HausMitFeuermelder
bekommt ein
Code:
Haus
als Construktorparameter und leitet alle Methoden, die in
Code:
Haus
deklariert sind an dieses übergebene Objekt weiter. IDE's können das generieren...

bye
TT
 
Danke euch, cklisch, Samuel und TT. Ich glaube, das sind Hinweise, mit denen ich etwas anfangen kann. Ob ich's damit hinkriege, oder noch weitere Hilfestellung brauche, werde ich heute im Verlaufe des Tages ausprobieren und mich dann noch einmal melden.

1000 Dank schonmal! 🙂
 
So, nun bin ich einen Schritt weiter. Die nächste Frage, die sich jetzt ergibt, ist, wie das erweiterteHaus nun die Eigenschaften des im Konstruktor übergebenen haus übernimmt. Müsste ich es, wie im folgenden Versuch machen und jede (in meinem Fall wären es zig verschiedene) Eigenschaft nach dem Schema this.x=haus.x kopieren oder gibt es eine Möglichkeit, das in einem Rutsch zu erledigen?

@ TT ich verwende Netbeans als IDE und habe mal kurz nach CoI recherchiert. Auf die Schnelle bin ich noch nicht fündig geworden, werde aber weiter suchen.

Vielen Dank für eure Hilfe! 🙂


Java:
public class ErweitertesHaus extends Haus {
 
    Boolean feuermelderInstalliert;
 
    ErweitertesHaus(Haus haus) {
            this.etagen=haus.etagen
            this.zimmer=haus.zimmer
            this.freistehend=haus.freistehend
            ...
            feuermelderInstalliert=false;
    }
}
 
@ TT ich verwende Netbeans als IDE und habe mal kurz nach CoI recherchiert. Auf die Schnelle bin ich noch nicht fündig geworden, werde aber weiter suchen.
CoI ist ein allgemeines Entwurfsprinzip.

Die nächste Frage, die sich jetzt ergibt, ist, wie das erweiterteHaus nun die Eigenschaften des im Konstruktor übergebenen haus übernimmt.
Gar nicht! Wie gesagt soll
Code:
ErweitertesHaus
Die Methoden, die
Code:
Haus
an dieses Weiterleiten. Auf Eigenschaften darf sowieso nicht von "außen" zugegriffen werden, so dass das auch kein Problem darstellen sollte...
Java:
public class ErweitertesHaus extends Haus {
 
    Boolean feuermelderInstalliert;
 
    ErweitertesHaus(Haus haus) {
            this.haus=haus.haus
            feuermelderInstalliert=false;
    }
    public boolean isFreistehend() { return this.haus.isFreistehend(); } // von IDE generieren lassen (generate delegation methods)
}
bye
TT
 
Also ich würde empfehlen, mit dem super-Konstuktor zu arbeiten, weil dann die Variablen nicht nochmal abgespeichert werden müssen.

Java:
public class ErweitertesHaus extends Haus {
 
    private boolean feuermelderInstalliert;
 
    public ErweitertesHaus(Haus haus) {
        this(haus, false);
    }

    public ErweitertesHaus(Haus haus, boolean isFeuermelderInstalliert){
        super(haus.getWert1(), haus.getWert2(),...); // Konstruktor aufrufen und falls Werde nicht mit dem 
                                                                       // Konstruktor gesetzt wurden mit den Settern setzen:
        setWert3(haus.getWert3());                         //...
        feuermelderInstalliert = isFeuermelderInstalliert;
    }

    // Getter und Setter für die neuen Variablen:
    public void setFeuermelderInstalliert(boolean isFeuermelderInstalliert){
        feuermelderInstalliert = isFeuermelderInstalliert;
    }

    public boolean getFeuermelderInstalliert(){
        return feuermelderInstalliert;
    }

    // Methoden, die dann im Haus schon implementiert wurden (wie z.b. isFreistehend) müssen dann hier nicht überschrieben werden, können
    // aber trotzdem verwendet werden.

}
 
@Timothy

dein Code würde aber vorraussetzen, dass in ErweitertesHaus eine Membervariable haus ist.
So müsstest du alle Methoden überschreiben und an diese Haus-Variable weiterleiten.
Somit nützt es dir doch eigendlich garnichts, dass du überhaupts von Haus erbst.
 
@Timothy

dein Code würde aber vorraussetzen, dass in ErweitertesHaus eine Membervariable haus ist.
So müsstest du alle Methoden überschreiben und an diese Haus-Variable weiterleiten.
Somit nützt es dir doch eigendlich garnichts, dass du überhaupts von Haus erbst.
Jein.
Die DelegationMethods sind perfomance-techinsch vernachlässigbar und können von der IDE generiert werden.

Das Erben von Haus ist tatsächlich nur deshalb notwendig, weil es hier (noch) keine Vererbung auf Interface-Ebene gibt, ich aber davon ausgehe, dass
Code:
ErweitertesHaus
auch Variablen vom Typ
Code:
Haus
zugewiesen werden soll.

Ich weis leider nicht, wie sich die Deserialisierung "alter"
Code:
Haus
-Objekte verhält, wenn man
Code:
Haus
ein neues Interface (
Code:
IHaus
?) implementieren lässt. Wenn das ginge würde ich nicht von Haus erben, sondern
Code:
IErweitertesHaus
von
Code:
IHaus
und
Code:
ErweitertesHaus
würde dann
Code:
IErweitertesHaus
implementieren. Das wäre das korrekte Design, wenn man CoI folgen will.

bye
TT
 
Wenn man aber von Haus erbt, und dann in der neuen Klasse wieder ein Haus Objekt hat,
verdoppelt sich der Speicherbedarf.
Denn vom geerbten Haus sind dann trotzdem die Variablen ja noch vorhanden.
Werden nur nicht gesetzt.
Das Haus Objekt welches im erweiterten Haus steckt hat nochmal den gleichen Satz an
Variablen.
Hierdurch würden Methoden falsch ausgeführt, falls es folgendermaßen aufgerufen wird:

Java:
Haus eHaus = new ErweitertesHaus(new Haus(10,12,true)); //erfundene Werte
eHaus.getEtagen();

im schlimmsten Fall wird eine NullpointerException geworfen, weil die Variable etagen im
ElternObject nicht initialisiert wurde, sondern blos in der Hausvariable, welche nur im
erweiterten Haus zu finden ist, gesetzt ist.
 
Wenn es nur der Speicherbedarf wäre, ok.
Aber hier könnten ja auch RuntimeExceptions ausgelöst werden.
Das ist nicht akzeptabel.
Aber nur, wenn ich in
Code:
ErweitertesHaus
auf direkt Properties von
Code:
Haus
zugreifen will. Dass sollte ich aber nicht können, weil die Properties ja
Code:
private
sein sollten
Wenn ich die (delegierten) getter benutze ist alles OK.

bye
TT
 
damit die Getter jedoch delegiert werden können, muss zwangsweise auf die Membervariablen von Haus
zugegriffen werden.
Auch wenn dafür die Getter vom Haus genutzt werden kann der Nullpointer auftreten.
 
damit die Getter jedoch delegiert werden können, muss zwangsweise auf die Membervariablen von Haus
zugegriffen werden.
Auch wenn dafür die Getter vom Haus genutzt werden kann der Nullpointer auftreten.
Jetzt verstehe ich was Du meinst...
Aber nein:
Java:
class ErweitertesHaus
  private final Haus haus;
  public ErweitertesHaus(Haus haus) { 
   if(null=haus) throw new InvalidParameterException("Haus darf nicht null sein");
   this.haus=haus;
} // haus ist nie null
bye
TT
 
Ich habe es so gemeint:

Ein Haus hat folgende Variablen:

Haus[adresse, anzahlEtagen]

dein ErweitertesHaus hat nun folgende Variablen:

ErweitertesHaus[adresse, anzahlEtagen, haus] die Variablen adresse und anzahlEtagen erbt es ja von Haus.

Wenn ich jetzt ein ErweitertesHaus konstruiere, sind die Variablen adresse und anzahlEtagen null.

Caste ich das ErweiteresHaus wieder zurück auf Haus und greife auf die Variablen zu
(egal ob direkt oder über getter), sind beide null.
 
Ich habe es so gemeint:

Ein Haus hat folgende Variablen:

Haus[adresse, anzahlEtagen]

dein ErweitertesHaus hat nun folgende Variablen:

ErweitertesHaus[adresse, anzahlEtagen, haus] die Variablen adresse und anzahlEtagen erbt es ja von Haus.
nein.
Code:
adresse
,
Code:
anzahlEtagen
, sind schon in
Code:
Haus
definiert, die werden im erweiterten Haus nicht mehr deklariert.

Wenn ich jetzt ein ErweitertesHaus konstruiere, sind die Variablen adresse und anzahlEtagen null.

Caste ich das ErweiteresHaus wieder zurück auf Haus und greife auf die Variablen zu
(egal ob direkt oder über getter)
das ist nicht egal. Die getter sind an das inner Haus-Objekt weitergeleitet und geben damit keine null zurück. Die Variablen selbst sind von außen nicht sichtbar.

Alles andere ist nicht OOP.
 
hm...anscheinend hast du recht.
Hab das wohl mit C++ durcheinander gebracht.
Da gehen beim Casten die zusätzlichen Informationen verloren und es werden die Methoden von
der Oberklasse verwendet.
Hier in Java ist das ErweiterteHaus nach dem Casten immer noch ein ErweitertesHaus und es
werden auch dessen Methoden verwendet.
Wenn man also den Speicherplatzverbrauch nicht nachtrauert und alle Methoden vom Haus
in der Unterklasse überschreibt und weiterreicht, wird kein Fehler auftreten.
Sry für meinen Irrtum.

P.S. lässt man die Vererbung von der Oberklasse weg, tritt auch kein Speicherplatz verloren.
 
Hab das wohl mit C++ durcheinander gebracht.
Da gehen beim Casten die zusätzlichen Informationen verloren und es werden die Methoden von
der Oberklasse verwendet.
Als bei den C++-Varianten, mit denen ich so gearbeitet habe war das Verhalten (beim Casten) genau so wie in Java. Dass muss auch so sein, weil es in C++ sonst keine Polymorphie geben könnte.

Was Du meinst ist, dass man nur noch auf Methoden zugreifen kann, die der Typ der Variablen deklariert. Genutzt werden aber die Implementierungen der Spezialisierung, wenn sie dort überschrieben sind, in Java und in C++...
Wie dass mit dem Zugriff auf Properties aussieht ist natürlich ein andere Sache, aber eben nicht OOP-konform...

bye
TT
 

Neue Themen


Zurück
Oben