Die leidige Referenz-Frage

  • Themenstarter Themenstarter Wachtertroll
  • Beginndatum Beginndatum
W

Wachtertroll

Gast
Hallo,

ich habe eine Frage zum beliebten Thema Referenzen in Java.

Mir ist klar, dass ich ein Objekt als Parameter an eine Funktion übergeben kann und dann innerhalb der Funktion das ursprüngliche Objekt änderne/lese und nicht eine lokale Kopie.

Jetzt will ich aber innerhalb eines Objekts einer Klasse eine Referenz auf ein Objekt außerhalb der Klasse speichern, weil ich dessen Eigenschaften später nutzen möchte.

Ein Beispiel in Pseudocode (das nichts mit meinem aktuellen Projekt zu tun hat *g*):

Code:
abstract class Dings { Position pos; }

class MutterDings extends Dings { ... }

class KindDings extends Dings { MutterDings mama; void gehZuMama(); }

ArrayList<Dings> alleDingse...

Und jetzt soll das KindDings beim Aufruf von gehZuMama() die Position des zugeordneten MamaDings auslesen, obwohl sich die Position von MamaDings seit dem Instanzieren geändert haben könnte.

In C würde man dazu einfach die Speicheradresse von MamaDings in einem Zeiger ablegen. Gibt es ein ähnliches Konstrukt in Java, oder muss ich das durch ganz anderes Programmdesign lösen? Die Referenz funktioniert ja wie gesagt genau andersherum.

Bei Interesse: es geht eigentlich darum, bei einem 2D-Spiel z. B. an die Koordinaten einer Spielfigur einen Spezialeffekt zu "pinnen", wobei beide von der Basisklasse "Actor" erben.
 
es ist nicht ersichtlich, inwiefern das Java-Referenz-Konzept dir da im Weg steht,

wie du selber sagst bedeutet Referenz 'das ursprüngliche Objekt änderne/lese und nicht eine lokale Kopie.'
wenn du die Mama als Referenz hast dann kennst du jederzeit deren aktuelle Position (keine Kopie eines alten Standes),
egal wer wo wann innerhalb außerhalb oder genau am Ende der Welt eine Änderung durchführt
 
es ist nicht ersichtlich, inwiefern das Java-Referenz-Konzept dir da im Weg steht,

wie du selber sagst bedeutet Referenz 'das ursprüngliche Objekt änderne/lese und nicht eine lokale Kopie.'
wenn du die Mama als Referenz hast dann kennst du jederzeit deren aktuelle Position (keine Kopie eines alten Standes),
egal wer wo wann innerhalb außerhalb oder genau am Ende der Welt eine Änderung durchführt

...und wenn du die Ursprungsposition behalten willst, musst du dir diese eben irgendwo/wie zusätzlich merken! ???:L:bahnhof:
 
Java Referenzen entsprechen in etwa den Zeigern in C/C++, aber zB. ohne Zeigerarithmetik.
Sowas wie die "C/C++ Referenzen" gibt es nicht in Java, auch braucht man eher selten Copy-Contruktoren und keinen Zuweisungsoperator.

Eine KindsDings Instanz kann auf die Instanz der impliziten DingsKlasse per [c]super[/c] Referenz zugreifen .
 
> Eine KindsDings Instanz kann auf die Instanz der impliziten DingsKlasse per super Referenz zugreifen .

naja, klingt ja fast wie zwei verschiedene Instanzen 😉
sind aber dieselben, mit super kann man doch nur super-Konstruktoren + höhere Versionen überschriebener Methoden aufrufen,
oder war noch mehr? viel ist das nicht
 
naja, klingt ja fast wie zwei verschiedene Instanzen 😉
Deswegen auch "implizit".

sind aber dieselben, mit super kann man doch nur super-Konstruktoren + höhere Versionen überschriebener Methoden aufrufen,
oder war noch mehr? viel ist das nicht
Wenn man das so sagen will, dann eher "niedrigere bzw. ältere Versionen" überschriebener Methoden.
super ist optional ausser wenn man Konstruktoren aufruft imho.

Wie dem auch sei, ich denke der TS hat Probleme mit der Java Referenz Semantik weil sie so ziemlich das Gegenteil von C/C++ Referenzen ist und dort als Zeiger verstanden wird.
 
Danke für eure Antworten!
Ich glaube, so einiges verstanden zu haben, während sich noch ein oder zwei Fragen aufgetan haben. (Und ich entschuldige mich im Voraus für meinen etwas längeren Post)

Ich habe gerade eben mein Pseudocodebeispiel in Java geschrieben und es funktioniert tatsächlich so, wie von SlaterB und euch anderen prophezeit.
Mit einem anderen Beispiel, das ich vorher geschrieben hatte, konnten Änderungen des Objekts allerdings nicht mehr nachvollzogen und abgerufen werden. Daher kamen meine ursprünglichen Zweifel am Klassensystem.
Mittlerweile denke ich, das könnte unter Umständen daran gelegen haben, dass ich einfach
Code:
int
statt Objekte benutzt hatte; allerdings konnte auch das Verwenden von
Code:
Integer
nichts daran ändern - eventuell liegt das am Autocasting der primitiven Datentypen?

Anscheinend habe ich - wie maki bemerkt hat - noch irgendwo einen Denkfehler dabei, wann ich mit einer Referenz arbeite und wann ich eine lokale Kopie erhalte; am Typ der Variable kann ich das ja nicht direkt sehen (vgl. Objekt vs. *Objekt in C). Ist es also so, dass ich aus der Art wie ich die Variable anlege selber mein Hirn benutzen und verstehen muss, was ich wann habe?

Mein Verständnis ist momentan in etwa so weit, und ich bitte um Korrektur oder Bestätigung:

[JAVA=42]
void whoIsYourMama(MutterDings mama)
{
// Referenz speichern:
this.erziehungsberechtigt = mama;

// Im Gegensatz dazu Objekt kopieren:
this.biologischeMama = new MutterDings(mama);
}
[/code]



Meine funktionierende Testklasse soweit:

[JAVA=42]
import java.util.ArrayList;

public class Testme
{
public static void main(String[] args)
{
abstract class Dings
{
int value = 0;
abstract void printOut();
abstract void doSomeThing();
}


class MutterDings extends Dings
{
public MutterDings(){}

public MutterDings(MutterDings ob)
{
this.value = ob.value;
}

// Aktion simulieren
void doSomeThing()
{
System.out.println("step");
value += 5;
}

// Eigenen Wert ausgeben
void printOut()
{
System.out.print("MD Value: " + value + " ");
}
}


class KindDings extends Dings
{
MutterDings parent;

KindDings(MutterDings parent)
{
this.parent = parent;
}

// Wert im Mutter-Objekt ausgeben
void printOut()
{
System.out.print("KD-M-Value: " + parent.value + " ");
}

void doSomeThing()
{
System.out.println("Nothing to do");
}
}


// Liste aus Dingern anlegen
ArrayList<Dings> zeug = new ArrayList<Dings>();
zeug.add(new MutterDings());
zeug.add(new KindDings((MutterDings)zeug.get(0)));

// Alles ausgeben
for (Dings d:zeug)
{
d.printOut();
}
System.out.println("");

// Der eigentliche Test
zeug.get(0).doSomeThing();

for (Dings d:zeug)
{
d.printOut();
}
}
}
[/code]

Führt wie erwartet zur Ausgabe

Code:
MD Value: 0    KD-M-Value: 0    
step
MD Value: 5    KD-M-Value: 5


Jetzt ist noch folgendes:

Wenn ich ein neues Objekt mit
Code:
new MutterDings()
anlege und deren Wert anschließend auf ein vorhandenes MutterDings setze erhalte ich wieder eine Referenz, obwohl ich ein Objekt angelegt habe.
Wenn ich also eine Kopie erstellen will muss ich den Konstructor
Code:
new MutterDings(altesMutterdings)
aufrufen, der explizit die Werte kopiert. Warum? Das könnte ein üble Fehlerquelle in komplexeren Programmen werden, falls man aus Versehen irgendwelche Werte ungewollt überschreibt... ist das der Grund für die Verbreitung von get() und set() Methoden?
Ist die Trennung zwischen dem Objekt und der Referenz nicht so scharf wie in anderen Sprachen? Meiner Vorstellung nach wird beim Anlegen eines Objekts Speicher reserviert und eine Referenz gespeichert, die sagt, wo dieses gefunden werden kann. Falls jetzt die Referenz auf den freien Speicher durch eine andere Referenz überschrieben wird - was passiert mit dem Speicher? Kann/muss ich den wieder freigeben?

Also, ich suche nicht unbedingt eine fachwissenschaftliche Abhandlung, mir würde schon ein genialer Hinweis, wie ich die Unterscheidung in der Praxis richtig hinbekomme, reichen... ;-)
 
Mittlerweile denke ich, das könnte unter Umständen daran gelegen haben, dass ich einfach
Code:
int
statt Objekte benutzt hatte; allerdings konnte auch das Verwenden von
Code:
Integer
nichts daran ändern - eventuell liegt das am Autocasting der primitiven Datentypen?

Vielleicht liegt das daran, dass der Wert eines Integers nicht geändert werden kann. Es kann nur ein neuer Integer (mit einem anderen Wert) angelegt und ggf. eine Referenz darauf gespeichert werden.

Ist die Trennung zwischen dem Objekt und der Referenz nicht so scharf wie in anderen Sprachen?

Die Trennung zwischen "Objekt" und "Referenz" ist genau so, wie in anderen Sprachen auch (d.h. zum Beispiel analog zu "Objekt" und "Zeiger" in C++).

Meiner Vorstellung nach wird beim Anlegen eines Objekts Speicher reserviert und eine Referenz gespeichert, die sagt, wo dieses gefunden werden kann.

Kann man so sagen, ja.

Falls jetzt die Referenz auf den freien Speicher durch eine andere Referenz überschrieben wird - was passiert mit dem Speicher? Kann/muss ich den wieder freigeben?

Eine Referenz auf freien Speicher ist ein Widerspruch in sich 😉 Entweder, dort, wo die Referenz "hinzeigt", liegt ein Objekt, oder die Referenz zeigt nirgendwo hin (ist also 'null').

Speicher manuell freigeben ist in Java nicht nötig. Sobald keine Referenz mehr auf ein existierendes Objekt zeigt, wird das Objekt gelöscht.
 
java ist ja nicht 100% sauber objektorientiert (wie z.B. smalltalk), sondern es gibt auch noch die primitiven Datentypen, die bei zuweisungen kopiert werden.

Wenn man schreibt

Objekt a=new Objekt();
Objekt b=a;

dann zeigen a und b auf dasselbe Objekt.

Hingegen

int a=5;
int b=a;

erzeug zwei verschiedene int-"Objekte". Wenn man a ändert, bleibt b davon unbeeinflussst.

Kanns daran liegen?
 
das passt eigentlich schon zusammen, einen int a zu ändern ginge nur mit Neuzuweisung,
genauso würde eine Neuzuweisung an die Object a-Variable b nicht ändern,

Objekte haben (meist) einen internen Zustand und können geändert werden, primitive Datentypen und manche Objekte wie Strings nicht
 

Zurück
Oben