Vererbung

fossybeer

Mitglied
Hallo Gemeinde!

Sicher ist das ein triviales Problem, aber ich komm nicht drauf und weiß auch nicht, unter welchem Stichwort ich nachschlagen/suchen soll. Es geht um das klassische Vererben:

Ich habe eine (Eltern-)Klasse "Fahrzeug" und zwei davon abgeleitete Klassen "PKW" und "LKW". Basisfunktionen stehen in "Fahrzeug", spezielle Funktionen in den Kindklassen, z.T. überschrieben, z.T. neu. Soweit so gut.

Nun kann der Nutzer auswählen, ob er einen PKW oder einen LKW berechnen will. Das Problem im Programm: Wie kann ich die richtige Klasse auswählen?

Code:
int zuBerechnendesFahrzeug = 1  // (1=PKW, 2=LKW);

if( zuBerechnendesFahrzeug == 1) 
{ 
   PKW Brumm = new PKW();
}
else
{ 
   LKW Brumm = new LKW();
}
So geht es NICHT, weil Brumm nur im if-Block gilt.



Code:
int zuBerechnendesFahrzeug = 1  // (1=PKW, 2=LKW);
Fahrzeug Brumm;

if( zuBerechnendesFahrzeug == 1) 
{ 
   Brumm = new PKW();
}
else
{
   Brumm = new LKW();
}
Und so kann ich nur auf die Methoden der Elternklasse zugreifen.

Was macht man bitte in so einem Fall? Das kommt doch sicher ständig vor, oder?
Ich stehe auf jeden Fall auf dem Schlauch. Danke euch!
 
Du musst deine ganzen Berechnungen innerhalb des If-Blocks machen, denn danach weist man ja nicht mehr, welche Auswahl getroffen wurde (deshalb kann Beispiel 2 nicht funktionieren). Also nimm Beispiel 1 und mach nach der Initialisierung weiter mit deinen Berechnungen. Du könntest dann auch beide Fälle in jeweils eine Funktion auslagern.
 
Danke für deine Antwort. Das ist allerdings recht unpraktikabel, weil ich dadurch viel Gleiches mehrfach tippen muß. Aber ich behalte es im Hinterkopf, wenn ich gar nicht mehr weiter komme.

Kann ich nicht irgendwie eine generische Fahrzeug-Klasse nutzen und erst später einer konkreten Klasse zuweisen. Ich weiß nicht...
 
Ich kenne mich damit nicht gut aus, aber ist es nicht so, dass nach der Vererbung die neuen Klassen als unabhängig gelten? Du kannst ja in deiner neuen Klasse "PKW" die Funktionen der Kind-Klasse "Fahrzeug" völlig neu gestalten. Also sind so gesehen "PKW" und "LKW" zwei völlig unterschiedliche Klassen.

Ich denke für deinen Anwendungszweck bietet sich die Vererbung, so wie du Sie verwenden willst, nicht an, da im If- bzw. Else-Block zwei völlig autonome Vorgänge geschehen.

Tipparbeit hast du ja nicht mehr, da du die jeweils gleichen Teile Kopieren kannst.

Aber wie gesagt, vielleicht meldet sich jemand mit mehr Fachwissen zu wort.
 
Hi,
Codeverdopplung solltest du tunlichst vermeiden
Was stellst du dir eigentlich und "Fahrzeug berechnen" vor?
Ciao
DosCoder
 
Danke für euere Antworten.

Die Fahrzeugberechnung ist nur ein Beispiel, damit ich mich nicht in unnötig komplexe Erklärungen verliere. In Wahrheit geht es um ein neuronales Netzwerk. Es wird ein Basisneuron mit der Grundfunktionalität definiert. Benutzt werden allerdings Neuronen verschiedenen Typs, die auf die z.T. vererbten Grundfunktionalitäten zurückgreifen. Berechnet wird dann der Output, der sich aus den Inputs ergibt - und diese Berechnung ist im Detail bei allen Typen unterschiedlich, die Grundfunktionen wie das Wichten der Eingänge, das Speichern der Nachbarneuronen etc. und die Grundattribute sind allerdings gleich. Daher habe ich auf die Vererbung zurückgegriffen.

Um bei dem Fahrzeugkonzept zu bleiben (ich glaube, das ist einfacher):
Ich möchte die laufenden Kosten ausrechnen. Bei beiden Typen haben eine Fahrleistung und ein Gewicht (in der Klasse "Fahrzeuge"), beim PKW berechnet sich der Benzinverbrauch nach [0,9 * Fahrleistung + 0,1*Gewicht], beim LKW nach [0,2 * Fahrleistung + 0,8*Gewicht]. Ich möchte also die Funktion Brumm.getVerbrauch() aufrufen und je nach Zugehörigkeit von Brumm das korrekte Ergebnis erhalten. Je nach Auswahl des Nutzers (zur Laufzeit), soll dazu die Klasse LKW oder die Klasse PKW benutzt werden. Es ist sicher nichts kompliziertes!
 
Zuletzt bearbeitet:
Du musst nur entsprechend genug im Interface definieren und eventuell noch eine abstrakte Klasse dazwischenpacken.

Java:
public interface Fahrzeug {

    double getWeighting();
    double getFactor();
    double getWeight();
    double getDistance();
    void setWeight(double weight);
    void setDistance(double distance);
    double calculateConsumption();

}

Java:
public abstract class CommonFahrzeug implements Fahrzeug {

    protected double weight;
    protected double distance;

    public double getWeight() {return weight;}
    public void setWeight(double weight) {this.weight = weight;}

    public double getDistance() {return distance;}
    public void setDistance(double distance) {this.distance = distance;}
    
    public double calculateConsumption() {
          return getWeighting() * getDistance() + getFactor() * getWeight();
    }
}

Java:
public class PKW extends CommonFahrzeug {
 
    public double getFactor() {return 0.8;}
    public double getWeighting() {return 0.1;}

}

Verwende bitte nicht irgendwelche Zahlen um einen Typ herauszufinden. Solche magic numbers machen den Code extrem unlesbar und unsicher. Denn man kann eine beliebige Zahl übergeben die eventuell gar nicht belegt ist.

Nutze stattdessen einen enum:
Java:
public enum FahrzeugTyp {
    PKW, LKW;
}

Java:
FahrzeugTyp type = PKW;

// Das final sorgt dafür, dass
// fahrzeug initialisiert werden muss
final Fahrzeug fahrzeug;
if(type == PKW) {
   fahrzeug = new PKW();
}
else if(type == LKW) {
   fahrzeug = new LKW();
}
else {
  throw new IllegalArgumentException("Unknown type");
}

fahrzeug.setDistance(10.5);
// etc....
 
An alle Antworter: VIELEN VIELEN DANK!

Ich finde es immer wieder aufs neue erstaunlich, wie viel Mühe sich viele Teilnehmer machen, um meine (!) Probleme zu lösen. Mit fällt es leider immer schwer, gute Tipps zu geben, aber ich bleibe dran.

Viele Grüße aus Hannover,
Alex
 
Das klappt super. Eins ist mir noch unschlüssig:
Du sagst:
Code:
final Fahrzeug fahrzeug;
Damit initialisierst du das Interface, was so nicht geht (oder?). Ich denke, dass ich
Code:
final Fahrzeug CommonFahrzeug;
machen muss. Das funktioniert dann auch. Was ich nun komisch finde ist, dass ich in CommonFahrzeug auch
Code:
setDistance(double);
definieren muss (auch wenn ich es aus dem Interface nehme), obwohl fahrzeug ja zum Zeitpunkt des Methodenaufrufes ja ein LKW und kein Common ist. Er nutzt diese Methode im CommonFahrzeug auch nie, läuft aber auch nicht ohne. Gibt es da eine einfache Erklärung? [Man will ja auch verstehen, wass man da so tut...]
----------------------------------------------------------------------------------------------------------------
Eins hätte ich noch in diesem Zusammenhang, bitte :rolleyes: :
Eine Methode haben alle Fahrzeuge identisch gemein. Diese Methode greift dabei auf fahrzeugspezifische Methoden zurück. Also z.B.

Code:
public double getGesamtkosten()
{
double gesamtkosten = 0.0;
gesamtkosten  += this.getFixkosten();
gesamtkosten  += this.getCummulatedConsumption();
return gesamtkosten;
}

Jetzt möchte ich diese Methode natürlich nicht bei jedem neuen Fahrzeug eintippen. Gibt es bitte eine Möglichkeit, diesen Methodenrumpf nur einmal zu schreiben?

Im Interface kann ich keinen Methodenrumpf vorgeben und in der abstrakten, bzw. Elternmethode kann ich mich nicht auf die Kind-Methoden (z.B. getFixkosten) beziehen.


Danke!

Alexander
 
Das klappt super. Eins ist mir noch unschlüssig:
Du sagst:
Code:
final Fahrzeug fahrzeug;
Damit initialisierst du das Interface, was so nicht geht (oder?). Ich denke, dass ich
Code:
final Fahrzeug CommonFahrzeug;
machen muss. Das funktioniert dann auch.

Wo ist da der Unterscheid, außer dass du der Variablen einen anderen Namen gegeben hast?

Das was da steht ist keine Initialisierung sondern nur eine Variablendeklaration. Damit wird keine neues Objekt erstellt.


Was ich nun komisch finde ist, dass ich in CommonFahrzeug auch
Code:
setDistance(double);
definieren muss (auch wenn ich es aus dem Interface nehme), obwohl fahrzeug ja zum Zeitpunkt des Methodenaufrufes ja ein LKW und kein Common ist. Er nutzt diese Methode im CommonFahrzeug auch nie, läuft aber auch nicht ohne. Gibt es da eine einfache Erklärung? [Man will ja auch verstehen, wass man da so tut...]

Also Fahrzeug ist ein Interface. Darin werden nur Methoden definiert, die konkrete Klasse die dieses Interface implementieren auch implementieren müssen. CommonFahrzeug ist eine abstrakte Klasse. Eine abstrakte Klasse ist keine konkrete Klasse und kann nicht direkt instanziiert werden. Abstrakte Klasse werden dafür benutzt um Methoden die von mehr als einer konkreten Klasse benötigt werden zu implementieren. Die konkreten Klassen erben dann von der abstrakten Klasse wie mit dem extends CommonFahrzeug angegeben. Die abstrakte Klasse muss die Methoden eines Interface nicht implementieren. Dafür müßten dann aber die konkreten Klassen die Methode implementieren.

Also folgendes:
Java:
public abstract class CommonFahrzeug implements Fahrzeug {
 
    protected double weight;
    protected double distance;
 
    public double getWeight() {return weight;}
    public void setWeight(double weight) {this.weight = weight;}
 
    public double getDistance() {return distance;}
   // kein setDistance hier!!
   
    public double calculateConsumption() {
          return getWeighting() * getDistance() + getFactor() * getWeight();
    }
}

Java:
public class PKW extends CommonFahrzeug {
 
    public double getFactor() {return 0.8;}
    public double getWeighting() {return 0.1;}
    // In der konkreten Klasse setDistance !
    public void setDistance(double distance) {this.distance = distance;}
 
}


Eins hätte ich noch in diesem Zusammenhang, bitte :rolleyes: :
Eine Methode haben alle Fahrzeuge identisch gemein. Diese Methode greift dabei auf fahrzeugspezifische Methoden zurück. Also z.B.

Code:
public double getGesamtkosten()
{
double gesamtkosten = 0.0;
gesamtkosten  += this.getFixkosten();
gesamtkosten  += this.getCummulatedConsumption();
return gesamtkosten;
}

Jetzt möchte ich diese Methode natürlich nicht bei jedem neuen Fahrzeug eintippen. Gibt es bitte eine Möglichkeit, diesen Methodenrumpf nur einmal zu schreiben?

Im Interface kann ich keinen Methodenrumpf vorgeben und in der abstrakten, bzw. Elternmethode kann ich mich nicht auf die Kind-Methoden (z.B. getFixkosten) beziehen.

Du kannst die Methode in die abstrakte Klasse packen. Alle Methoden die du in dieser Methode benutzt, müssen aber der Klasse bekannt sein. Also entweder musst du sie im Interface definieren (wovon ja deine abstrakte Klasse erbt) oder in der abstrakten Klasse selber, ähnlich wie im Interface mit abstrak davor.
 
Zurück