Konstruktoren überladen
Lösung geprüft
Die Aufgabe der heutigen Stunde ist das Überladen von Konstruktoren! Beim Dreieck haben wir das eben schon nebenbei gemacht, jetzt schauen wir uns an, warum das überhaupt geht und wo die Fallstricke liegen.
Die Idee dahinter ist simpel: eine Klasse darf mehrere Konstruktoren
haben, solange sich deren Parameterlisten unterscheiden – in der
Anzahl, in den Typen oder in der Reihenfolge. Java sucht sich beim
new selbst den passenden raus.
a) Beschreiben (nicht programmieren) Sie ein weiteres sinnvolles Beispiel zur Konstruktor-Überladung mit Datentypen.
b) Programmieren Sie das sinnvolle Beispiel für die Überladung von einem Konstruktor.
Was vorgegeben ist
Als Beispiel dient ein Rechteck mit drei Konstruktoren:
public class Rechteck {
// Defaultwerte
private double breite = 1;
private double hoehe = 1;
// Konstuktor 1 Parameter: double, double
public Rechteck(double breite, double hoehe) {
if (breite >= 0 && hoehe >= 0) {
this.breite = breite;
this.hoehe = hoehe;
}
}
// Konstuktor 2 Parameter: double, double
public Rechteck(int breite, int hoehe) {
if (breite >= 0 && hoehe >= 0) {
this.breite = breite;
this.hoehe = hoehe;
}
}
// Konstuktor 3 Parameter: double
public Rechteck(double seite) {
if (seite >= 0) {
this.breite = seite;
this.hoehe = seite;
}
}
}
Konstruktor 3 ist der interessanteste: ein Rechteck mit nur einer Seitenangabe ist ein Quadrat. Man spart sich also eine eigene Klasse.
classDiagram
class Rechteck {
-double breite = 1
-double hoehe = 1
+Rechteck(double, double)
+Rechteck(int, int)
+Rechteck(double)
}
Alle drei heißen Rechteck, und trotzdem kann Java sie
auseinanderhalten: der erste nimmt zwei double, der zweite
zwei int, der dritte nur einen Wert. Die Parameterliste
ist das einzige Unterscheidungsmerkmal.
Lösungsprozess der Aufgabe
/**
*
* Konstruktor-Ueberladung am Beispiel eines Bankkontos
*
* @version 1.0 vom 25.08.2025
* @meininfokurs.cmrg.site
*/
public class Konto {
// Defaultwerte, falls ein Konstruktor nichts setzt
private String inhaber = "Unbekannt";
private double kontostand = 0;
private double dispo = 0;
// Konstruktor 1 - Parameter: String
// Neues Konto, nur der Name ist bekannt. Startet bei 0 Euro.
public Konto(String inhaber) {
if (inhaber != null && !inhaber.equals("")) {
this.inhaber = inhaber;
}
}
// Konstruktor 2 - Parameter: String, double
// Konto mit Startguthaben, zum Beispiel bei einem Bankwechsel.
public Konto(String inhaber, double kontostand) {
if (inhaber != null && !inhaber.equals("")) {
this.inhaber = inhaber;
}
this.kontostand = kontostand;
}
// Konstruktor 3 - Parameter: String, double, double
// Konto mit Startguthaben und eingeraeumtem Dispo.
public Konto(String inhaber, double kontostand, double dispo) {
if (inhaber != null && !inhaber.equals("")) {
this.inhaber = inhaber;
}
this.kontostand = kontostand;
if (dispo >= 0) {
this.dispo = dispo;
}
}
public double getKontostand() {
return kontostand;
}
public void einzahlen(double betrag) {
if (betrag <= 0) {
System.out.println("Fehler: Der Betrag muss positiv sein.");
return;
}
kontostand += betrag;
}
public void abheben(double betrag) {
if (betrag <= 0) {
System.out.println("Fehler: Der Betrag muss positiv sein.");
return;
}
if (kontostand - betrag < -dispo) {
System.out.println("Fehler: Das ueberzieht den Dispo.");
return;
}
kontostand -= betrag;
}
public void ausgabe() {
String info = "Konto von " + inhaber + "\n"
+ " Kontostand: " + InOut.format2(kontostand) + " Euro\n"
+ " Dispo: " + InOut.format2(dispo) + " Euro";
System.out.println(info);
}
} // end of class Konto
Teil a) – das Beispiel beschreiben
Gefragt ist erstmal Text, kein Code. Mein Beispiel ist ein Bankkonto, und zwar aus folgendem Grund: es gibt wirklich drei verschiedene Situationen, in denen ein Konto entsteht, und in jeder weiß man unterschiedlich viel.
| Konstruktor | Parameter | Wann man ihn braucht |
|---|---|---|
| 1 | String | Konto frisch eröffnet, es liegt noch kein Geld drauf |
| 2 | String, double | Bankwechsel – das Guthaben kommt mit |
| 3 | String, double, double | Konto mit eingeräumtem Dispo |
Alle drei unterscheiden sich in der Anzahl der Parameter. Das ist die einfachste und sicherste Art zu überladen, da kann sich Java nicht vertun.
Die Defaultwerte oben
Der Trick aus dem Rechteck-Beispiel funktioniert hier genauso: die Attribute bekommen schon bei der Deklaration einen sinnvollen Wert.
private String inhaber = "Unbekannt";
private double kontostand = 0;
private double dispo = 0;
Dadurch muss nicht jeder Konstruktor jedes Attribut setzen. Konstruktor 1 kümmert sich nur um den Namen, Kontostand und Dispo bleiben automatisch bei 0. Ohne Defaultwerte müsstest du in jedem Konstruktor alles nochmal hinschreiben – und vergisst dann garantiert eins.
Die zwei Regeln, die man wissen muss
Erstens: der Rückgabetyp zählt nicht. Zwei Methoden, die sich nur im Rückgabetyp unterscheiden, sind für Java dieselbe Methode und das ist ein Fehler. Bei Konstruktoren ist das sowieso egal, die haben gar keinen – bei normalen Methoden ist es aber eine beliebte Falle.
Zweitens: sobald du selbst einen Konstruktor schreibst, ist der automatische weg. Solange eine Klasse gar keinen Konstruktor hat, spendiert Java stillschweigend einen parameterlosen:
public Rechteck() {} // macht Java selbst - aber nur, solange du keinen eigenen schreibst
Der erzeugt nur das Objekt und initialisiert nichts. Schreibst du einen
eigenen Konstruktor, verschwindet der. Wenn du dann irgendwo
new Konto() ohne Argumente schreibst, meckert der Compiler –
und die Fehlermeldung ist ziemlich unverständlich, wenn man nicht weiß
woran es liegt. Willst du beides haben, musst du den leeren Konstruktor
selbst dazuschreiben.
Vorsicht bei int und double
Im vorgegebenen Rechteck stehen Konstruktor 1 mit
(double, double) und Konstruktor 2 mit
(int, int). Das ist erlaubt, aber es ist auch das Beispiel,
bei dem man am ehesten ins Grübeln kommt. Was passiert bei
new Rechteck(3, 4)?
Da 3 und 4 Ganzzahlen sind, nimmt Java
Konstruktor 2. Erst wenn kein int-Konstruktor da wäre,
würde Java die Werte automatisch in double umwandeln und
Konstruktor 1 nehmen. Schreibst du dagegen
new Rechteck(3.0, 4.0), ist Konstruktor 1 dran.
Deshalb habe ich das in meinem Konto-Beispiel bewusst nicht gemacht.
Überladen über die Anzahl der Parameter ist eindeutig; überladen über
ähnliche Zahlentypen sieht clever aus, sorgt aber für Code, bei dem man
zweimal hinschauen muss. Und der Kommentar über Konstruktor 2 im
Rechteck ist übrigens falsch – da steht „Parameter: double, double",
obwohl int, int gemeint ist. Genau so entstehen Missverständnisse.