MINT lernen

Kapselung mit get und set

Was hält ein Objekt eigentlich davon ab, sich eine Note 7 oder ein Guthaben von −20 € unterschieben zu lassen?

Dein Fortschritt:
0 / 0 Aufgaben
1

Übungsaufgaben

Zehn Übungen zum Klicken, Ziehen und Knobeln — von AFB I bis AFB III. Jede Übung gibt dir sofort Rückmeldung; wenn du hängst, helfen dir die gestuften Tipps.

A1
Getter, Setter oder weder noch?
AFB I

Die Quiz-App der Schule speichert jedes Spielerkonto als Objekt. Sortiere die Methodenköpfe.

Ziehe jede Karte in den passenden Korb — oder wähle sie mit Enter aus und drücke dann die Ziffer des Korbs (0 legt sie zurück).
1Getter
2Setter
3weder noch
Getter: kein Parameter, Rückgabetyp wie das Attribut, Name getX() — bei Wahrheitswerten istX(). Setter: void, genau ein Parameter, Name setX(…). levelAufsteigen() ändert zwar ein Attribut, ist aber eine fachliche Methode; berechneBonus(…) rechnet etwas aus, statt ein Attribut zu liefern.
Ansatz: Achte zuerst auf den Namen: Beginnt er mit get, ist oder set?
Weiter: Prüfe dann die Form: Getter ohne Parameter mit Rückgabe, Setter mit void und einem Parameter.
A2
Stimmt's? — Das Schließfach
AFB I

Die Klasse Schliessfach hat die privaten Attribute code (String) und belegt (boolean). Fünf Behauptungen:

Fünf Aussagen nacheinander. Eine falsche Einschätzung reicht — dann startest du die Serie mit „Neue Runde“ neu.
Aussage 1 von 5

Kapselung heißt: Die Klasse entscheidet, was von außen erlaubt ist. Nur lesen = Getter ohne Setter, nur schreiben = Setter ohne Getter, gar nichts = keins von beiden. Der Compiler setzt das schon beim Übersetzen durch.
Ansatz: private trennt „innerhalb der Klasse“ von „außerhalb“. Wer prüft das — javac oder die laufende JVM?
Weiter: Getter und Setter sind unabhängig voneinander. Und für Wahrheitswerte gibt es eine eigene Namensregel.
A3
Memory: Attribut sucht Getter
AFB I

Zu jedem privaten Attribut eines Fahrrad-Leihsystems gehört genau ein Getter-Kopf. Finde die Paare.

Decke zwei Karten auf, die zusammengehören. Mit der Tastatur: Tab zur Karte, Enter aufdecken, Pfeiltasten zum Wandern.
Der Rückgabetyp des Getters ist immer der Typ des Attributs — auch bei char. Aus einem Attribut wird ein Getter, indem man get (bzw. ist) davorsetzt und den ersten Buchstaben großschreibt.
Ansatz: Merke dir beim Aufdecken den Datentyp — er steht beim Attribut vor dem Namen, beim Getter nach public.
Weiter: Es gibt zwei boolean-Paare. Dort entscheidet der Name hinter ist.
A4
Sechs oder zwölf Saiten
AFB II

Die Musikschule verleiht Gitarren. Eine Gitarre hat entweder 6 oder 12 Saiten — andere Werte soll der Setter abweisen. Ergänze die Klasse; zwei Bausteine bleiben übrig.

Wort anklicken, dann Lücke anklicken (oder umgekehrt) — mit Tab und Enter geht es genauso. Ein Klick auf eine gefüllte Lücke legt das Wort zurück.

public class Gitarre {
     int saiten;
    public getSaiten() { return saiten; }
    public void setSaiten(int saiten) {
        if (saiten == 6 saiten == 12) {
            .saiten = saiten;
        }
    }
}

Hier ist ausnahmsweise || richtig: Erlaubt sind zwei einzelne Werte, und eine Zahl kann nicht gleichzeitig 6 und 12 sein — mit && würde der Setter jeden Wert abweisen. Bei einem Bereich („von 1 bis 14“) ist es umgekehrt, dort braucht man &&. Der Getter liefert einen int, also nicht void. (Mit javac geprüft.)
Ansatz: Attribut verstecken, Getter mit dem Typ des Attributs — das sind die ersten beiden Lücken.
Weiter: Teste die Bedingung mit 6, mit 12 und mit 7: Welcher Operator lässt genau die ersten beiden durch?
A5
Die Mensakarte
AFB II

Auf eine Mensakarte passen höchstens 50 €; ins Minus darf sie nicht rutschen.

public class Mensakarte {
    private double guthaben;

    public void setGuthaben(double guthaben) {
        if (guthaben >= 0 && guthaben <= 50) {
            this.guthaben = guthaben;
        }
    }

    public void aufladen(double betrag) {
        setGuthaben(guthaben + betrag);
    }

    public void bezahlen(double preis) {
        setGuthaben(guthaben - preis);
    }

    public double getGuthaben() {
        return guthaben;
    }
}
Mensakarte k = new Mensakarte();
k.aufladen(20);
k.bezahlen(3.5);
k.aufladen(40);
k.bezahlen(4.25);
k.bezahlen(15);
k.aufladen(30);

Welchen Wert liefert danach k.getGuthaben()?

Rechne selbst und trage das Ergebnis ein (Dezimalkomma oder -punkt) — Enter prüft direkt.
Verlauf: 0 → 20 → 16,5 → (56,5 abgewiesen) 16,5 → 12,25 → (−2,75 abgewiesen) 12,25 → 42,25. Häufige Fehlantworten: 50 (wer glaubt, der Setter „deckelt“ auf 0 bzw. 50) oder 67,25 (wer die abgewiesenen Aufrufe trotzdem mitrechnet). Weil aufladen und bezahlen den Setter benutzen, gilt die Prüfung auch intern. (Ausgabe mit java geprüft: 42.25.)
Ansatz: Notiere nach jeder Zeile das Guthaben. Rechne bei jedem Aufruf erst den neuen Wert aus und prüfe ihn dann gegen 0 bis 50.
Weiter: Ein abgewiesener Wert ändert gar nichts — das Guthaben bleibt, wie es war. Zwei der sechs Aufrufe werden abgewiesen.
A6
Artikel im Online-Shop
AFB II

Für die Klasse Artikel gibt es Anforderungen an jedes Attribut. Kreuze an, was die Klasse braucht.

Setze die passenden Kreuze. Enter setzt und löscht.
AnforderungprivateGetterSetter
nummer: beim Anlegen vergeben, danach nur lesbar
preis: lesbar und änderbar, nie negativ
bestand: lesbar, ändert sich nur durch verkaufen()
rabatt: darf gesetzt werden (0 bis 50), wird nie ausgelesen
notiz: nur für die Klasse selbst
Alle Attribute sind private — Getter und Setter werden dann bewusst ergänzt, je nach Anforderung. bestand braucht keinen Setter: Sonst könnte jemand von außen den Bestand beliebig „herbeizaubern“. Getter und Setter sind keine Pflicht-Paare.
Ansatz: Die erste Spalte ist schnell erledigt: Welche Attribute sollten in einer gekapselten Klasse private sein?
Weiter: Lesbar von außen → Getter. Von außen direkt setzbar → Setter. „Nur durch eine andere Methode“ heißt: kein Setter.
A7
Schul-Shirts bestellen
AFB IIMix

Wiederholung aus 1.1.2 und 1.1.3 (Datentypen, Bedingungen, equals): Für das Schul-Shirt gibt es drei Größen; bestellen dürfen die Jahrgänge 5 bis 10. Was passiert bei den Aufrufen an einer Bestellung b aus einer anderen Klasse?

public void setGroesse(String groesse) {
    if (groesse.equals("S") || groesse.equals("M") || groesse.equals("L")) {
        this.groesse = groesse;
    }
}

public void setJahrgang(int jahrgang) {
    if (jahrgang >= 5 && !(jahrgang > 10)) {
        this.jahrgang = jahrgang;
    }
}
Ordne jeden Aufruf einem der drei Bereiche zu. Mit der Tastatur: Enter zum Auswählen, dann 1–3 für den Bereich, 0 zurück.
1 Wert wird übernommen
2 läuft, Wert wird abgewiesen
3 Übersetzungsfehler
equals unterscheidet Groß- und Kleinschreibung: "m" ist nicht "M". !(jahrgang > 10) bedeutet „höchstens 10“ — 10 ist erlaubt, 11 nicht. Die drei Übersetzungsfehler kommen aus ganz verschiedenen Ecken: 'M' ist ein char, kein String (incompatible types: char cannot be converted to String), "7" ist Text statt Zahl, und b.jahrgang scheitert an private (jahrgang has private access). (Alle Fälle mit javac/java geprüft.)
Ansatz: Prüfe zuerst, ob der Aufruf überhaupt übersetzt wird: Passt der Typ des Arguments? Wird ein privates Attribut direkt angefasst?
Weiter: Einfache Anführungszeichen bedeuten char. Und equals vergleicht Buchstabe für Buchstabe — auch Groß und Klein.
A8
Fehlersuche: Die WLAN-Karte
AFB III

Anforderungen an die Klasse WlanKarte: Der Benutzername ist nach dem Anlegen nur lesbar; das Minutenguthaben (0 bis 600) ist lesbar und darf nur gültige Werte annehmen — ungültige werden abgewiesen; gesperrt wird eine Karte nur über sperren(). Drei Zeilen verletzen die Anforderungen.

Klicke genau die fehlerhaften Zeilen an — die richtigen musst du stehen lassen.
Keine der drei Zeilen erzeugt einen Übersetzungsfehler — alle verstoßen gegen die Anforderungen. Besonders tückisch ist Zeile 7: Wer setMinuten(-5) aufruft, bekommt plötzlich 600 Minuten geschenkt. Zeile 9 ist richtig: gesperrt braucht keinen Setter, weil nur sperren() es ändern soll.
Ansatz: Gehe die Anforderungen einzeln durch und suche jeweils die Zeilen, die dazu gehören.
Weiter: Prüfe: Sind alle Attribute wirklich geschützt? Gibt es einen Setter, der nicht da sein dürfte? Was macht jeder Setter mit einem ungültigen Wert wie −5?
A9
Der Akku des E-Rollers
AFB III

Der Akku eines E-Rollers fasst 400 Wh; jeder Kilometer verbraucht 20 Wh.

public class ERoller {
    private int ladung;   // in Wh, 0 bis 400
    private int km;

    public ERoller() {
        ladung = 400;
    }

    public void setLadung(int ladung) {
        if (ladung >= 0 && ladung <= 400) {
            this.ladung = ladung;
        }
    }

    public void fahren(int strecke) {
        if (strecke * 20 <= ladung) {
            setLadung(ladung - strecke * 20);
            km = km + strecke;
        }
    }

    public int getProzent() {
        return ladung * 100 / 400;
    }

    public int getKm() {
        return km;
    }
}
ERoller r = new ERoller();
r.fahren(7);
r.fahren(15);
r.fahren(12);
int p1 = r.getProzent();
r.setLadung(450);
r.setLadung(150);
int p2 = r.getProzent();
Rechne die Kette Schritt für Schritt: Erst wenn ein Schritt stimmt, wird der nächste freigeschaltet. Enter prüft.
  1. ladung nach fahren(7) Wh
  2. r.getKm() nach allen drei Fahrten km
  3. Wert von p1 %
  4. Wert von p2 %
400 − 140 = 260 Wh. fahren(15) bräuchte 300 Wh und wird von fahren selbst abgelehnt — auch km bleibt dann unverändert. fahren(12) braucht 240 Wh: Rest 20 Wh, 7 + 12 = 19 km, 20 · 100 / 400 = 5 %. setLadung(450) wird abgewiesen, 150 übernommen. Letzte Falle: 150 · 100 / 400 = 37,5 — aber int-Division schneidet ab, also 37. (Mit java geprüft.)
Ansatz: Führe eine kleine Tabelle mit ladung und km. Bei jeder Fahrt: Reicht die Ladung für strecke · 20?
Weiter: getProzent() rechnet nur mit int-Werten. Was macht Java mit dem Rest einer Ganzzahl-Division?
A10
Trickaufgabe: Das Thermostat im Klassenraum
AFB IIITrick

Erlaubt sind 5 bis 30 °C. Jede Zeile startet mit einem neuen Thermostat. Welchen Wert liefert danach getGrad()?

public class Thermostat {
    private int grad;

    public Thermostat(int grad) {
        this.grad = grad;
    }

    public void setGrad(int grad) {
        if (grad >= 5 && grad <= 30) {
            this.grad = grad;
        }
    }

    public void waermer() {
        setGrad(grad + 1);
    }

    public int getGrad() {
        return grad;
    }
}
Trage in jedes Feld den Rückgabewert ein und klicke auf „Prüfen“ — Enter geht auch.

a) new Thermostat(21), dann zweimal waermer() →

b) new Thermostat(35) →

c) new Thermostat(35), dann waermer() →

d) new Thermostat(35), dann setGrad(29) und waermer() →

e) new Thermostat(2), dann waermer() →

Der Trick: Der Konstruktor weist direkt zu und umgeht die Prüfung — ein Thermostat mit 35 °C oder 2 °C entsteht einfach. Danach wird jeder weitere Schritt vom Setter geprüft: 36 wird abgewiesen (c), 3 ebenfalls (e) — der ungültige Wert bleibt also stecken. Erst ein gültiger setGrad-Aufruf „rettet“ das Objekt (d). Abhilfe: im Konstruktor setGrad(grad); aufrufen. (Alle fünf Fälle mit java geprüft.)
Ansatz: Schau dir den Konstruktor genau an: Prüft er den übergebenen Wert?
Weiter: waermer() geht durch den Setter. Liegt grad + 1 außerhalb von 5 bis 30, bleibt der alte Wert — auch wenn der schon ungültig war.