Instanzattribut per Getter Methode zuweisbar, warum?

Okury

Neues Mitglied
Guten Tag,

ich versuche auf ein Array per Getter Methode zuzugreifen, um darin Parameter zu speichern/ändern. Daraus ergibt sich ein Verständnisproblem.

  1. Es wurde eine Instanz (car1) von Car per Default Konstruktor erstellt. Darüber sind die Instanzmethoden & -attribute generell erreichbar (von sichtbarkeits Operatoren abgesehen), stimmt das?
  2. Instanzattribute sind per private Anweisung zu schützen. (Car.java Zeile 2 & 3)
  3. Die Zuweisung von car1.getHorsePower()=23; schlägt wie erwartet fehl, da es eine Getter Methode ist. Über car1.setHorsePower(23); funktioniert die Zuweisung wie erwartet.
  4. Über den Getter car1.getWheels()[1] = 2; kann ich der Instanzvariable den Wert 2 zuweisen. Das sollte über den Getter doch nicht möglich sein. Und hier liegt mein Verständnisproblem.
  5. Die Ausgabe zeigt 2 und 23. Das ist auch wie erwartet.
Da ich davon ausgehe das Java das tut was es tuen soll, muss mein Verständnis falsch sein. Kann hier jemand etwas Licht ins Dunkle bringen.

Hier noch der zugehörige Code.

Vielen Dank

[CODE lang="java" title="HelloWorld.class (Java Code)" highlight="5,6"]public class HelloWorld {
public static void main(String[] args) {
Car car1 = new Car();

car1.getHorsePower()=234; // zuweisen auf getHorsePower() nicht Möglich
car1.getWheels()[1] = 2; // zuweisen auf getWheels()[1] ist Möglich

car1.setHorsePower(23);
System.out.println(car1.getWheels()[1]);
int hp = car1.getHorsePower();
System.out.println(hp);
}
}[/CODE]

[CODE lang="java" title="Car.java (Java Klasse)" highlight=""]public class Car {
private int horsePower;
private int[] wheels = {0,1,2,3};

public int[] getWheels() {
return wheels;
}
public void setWheels(int[] wheels) {
this.wheels = wheels;
}
public int getHorsePower() {
return horsePower;
}
public void setHorsePower(int horsePower) {
this.horsePower = horsePower;
}
}[/CODE]
 
Dein Punkt 4 funktioniert, weil getWheels() ein Array zurückgibt. Du versuchst nun aber nicht, den Rückgabewert der Methode zu ändern, vielmehr greifst Du mit Hilfe des Index-Operators ([]) in das zurückgegebene Array hinein und änderst einen Wert.

Das ist, wie wenn Du mir Dein Portemonnaie gibst. Ich kann daraus kein anderes zaubern, aber ich kann sehr wohl den Inhalt ändern 🙂
 
Zuletzt bearbeitet:
Und vor allem verhält sich ein Array wie ein Objekt, d.h. die Arrayvariable enthält nicht das Array, sondern eine Referenz auf das Array (also grob gesagt, einen Verweis auf den Speicherort, an dem sich der tatsächliche Inhalt des Arrays befindet).

Dein Getter gibt eine Kopie dieser Referenz zurück, d. h. sowohl die private Arrayvariable, als auch die Rückgabe "zeigen" auf den selben Inhalt. Darum kannst du über diese Referenz auch den Inhalt der privaten Variable ändern.

Möchtest du das verhindern, dann musst du eine (Referenz auf eine) Kopie des Arrays zurück geben. Die kann man dann zwar auch ändern, aber das private Original bleibt so wie es ist.

Java:
// dieses hier:
car1.getWheels()[1] = 2; // zuweisen auf getWheels()[1] ist möglich

// entspricht diesem:

int[] wheels = car1.getWheels(); // gibt die Referenz auf das interne Array zurück
wheels[1] = 2; // ändert den Inhalt

Probier doch mal:
Java:
public int[] getWheels() {
    return wheels.clone();
}
 
Zuletzt bearbeitet:
Also dazu noch einmal ein paar Sichten:

a) rein logisch: Die Methode kann ja einfach den Wert eines Literals zurück geben:
public int getNumber { return 42; }
Das sollte aufzeigen, dass eine Zuweisung keinen Sinn machen kann, denn wo sollte da der Wert hin geschrieben werden?

b) Java Spezifikation: Nach der Java Spezifikation gibt es keine Zeiger sondern nur Values und Referenzen. Referenzen sind dabei von der JVM verwaltete Adressen, an denen komplexe Typen (Instanzen oder Array) zu finden sind. Generell werden diese Werte immer nur kopiert, d.h. egal ob bei einer Zuweisung, einem Parameter oder einer Rückgabe: Genau dieser Wert wird KOPIERT. Bei Parametern hat dies einen expliziten Namen: Call by Value. Du kannst mit diesen Werten diverse Dinge machen: Diese in einer Variablen speichern, woanders hin übergeben oder direkt in einer Operation nutzen. Wie @mihe7 schon sagte: Die [] sind einer Operation auf der Array Referenz.

c) Aus der Clean Code Sicht: Es gibt Getter und Setter. Über den Getter einen Wert verändern wäre ein wenig dumm. Wenn ich sage: Gib mit 10€, dann ist die Erwartungshaltung, dass Du mir Geld gibst. Da ist die Erwartungshaltung nicht, dass Du mir alle Taschen leer machst. Das wäre dann halt nicht "Gib mir 10€" sondern ein "Raube mich aus". Also ein klares Unterschied und diesen Unterschied gibt es natürlich auch zwischen Getter und Setter.
 
Und vor allem verhält sich ein Array wie ein Objekt, d.h. die Arrayvariable enthält nicht das Array, sondern eine Referenz auf das Array (also grob gesagt, einen Verweis auf den Speicherort, an dem sich der tatsächliche Inhalt des Arrays befindet).

Dein Getter gibt eine Kopie dieser Referenz zurück, d. h. sowohl die private Arrayvariable, als auch die Rückgabe "zeigen" auf den selben Inhalt. Darum kannst du über diese Referenz auch den Inhalt der privaten Variable ändern.

Möchtest du das verhindern, dann musst du eine (Referenz auf eine) Kopie des Arrays zurück geben. Die kann man dann zwar auch ändern, aber das private Original bleibt so wie es ist.

Java:
// dieses hier:
car1.getWheels()[1] = 2; // zuweisen auf getWheels()[1] ist möglich

// entspricht diesem:

int[] wheels = car1.getWheels(); // gibt die Referenz auf das interne Array zurück
wheels[1] = 2; // ändert den Inhalt

Probier doch mal:
Java:
public int[] getWheels() {
    return wheels.clone();
}
und
3. schlägt fehl, weil primitive Datentypen per se unveränderbar sind.
 
Und vor allem verhält sich ein Array wie ein Objekt, d.h. die Arrayvariable enthält nicht das Array, sondern eine Referenz auf das Array (also grob gesagt, einen Verweis auf den Speicherort, an dem sich der tatsächliche Inhalt des Arrays befindet).

Dein Getter gibt eine Kopie dieser Referenz zurück, d. h. sowohl die private Arrayvariable, als auch die Rückgabe "zeigen" auf den selben Inhalt. Darum kannst du über diese Referenz auch den Inhalt der privaten Variable ändern.

Möchtest du das verhindern, dann musst du eine (Referenz auf eine) Kopie des Arrays zurück geben. Die kann man dann zwar auch ändern, aber das private Original bleibt so wie es ist.

Java:
// dieses hier:
car1.getWheels()[1] = 2; // zuweisen auf getWheels()[1] ist möglich

// entspricht diesem:

int[] wheels = car1.getWheels(); // gibt die Referenz auf das interne Array zurück
wheels[1] = 2; // ändert den Inhalt

Probier doch mal:
Java:
public int[] getWheels() {
    return wheels.clone();
[/QUOTE]
und:
3. schlägt fehl, weil primitive Datentypen per se unveränderbar sind.
 
Arrays sind im Normalfall eh keine gute Idee.
Ein Array kannst du nicht vor Schreibzugriff schützen.
@davidchsg hat bereits eine gute Möglichkeit genannt, wie du zumindest potentiell negative Auswirkungen verhindern kannst, du lieferst nicht das Array selbst, sondern eine Kopie davon.
Persönlich hätte ich zu System.arraycopy gegriffen, aber das Klonen macht wahrscheinlich auch nichts anderes, hab grad nicht die Muse, das zu prüfen.

Eine Alternative wäre der Verzicht auf Arrays und das Arbeiten mit Listen.
Hier hast du nämlich eine ziemlich geniale Methode verfügbar.

Java:
public List<Integer> getWheels() {
    return Collections.unmodifiableList(myListOfWheels);
}

Hier wird die Liste in einen Wrapper gepackt, der beim Versuch, darauf zu schreiben, eine Exception auslöst.
Ähnliche Methoden gibt's für Maps und Sets.


Wichtig: Das Array selbst enthält in diesem Fall nur integer-Werte, kein großes Ding. Aber wenn du z.B. Points lieferst, dann können die Werte dieser Points auch weiterhin geändert werden.

Code:
myObject.getPoint().setLocation(4,5)
ist sehr oft erwünscht, aber nicht immer (ich versuch so was nach Möglichkeit zu vermeiden, sowohl bei set als auch bei get werden im Normalfall Kopien angelegt, bzw. die Werte übertragen und nicht das Objekt direkt zugewiesen)


Du müsstest auch jedes einzelne Element dieses Arrays oder der Liste entweder schützen oder kopieren (Stichwort DeepCopy/ShallowCopy/FlatCopy).
 
Zuletzt bearbeitet:

Zurück
Oben