PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [Java] komplette Array-Kopie


Senior Sanchez
2006-03-09, 21:27:45
Hi,

Ich würde gerne ein Array von Objekten komplett (!) kopieren, also nicht nur die Referenzen sondern es sollen neue Objekte mit dem gleichen Inhalt sein.

System.arrayCopy() kopiert ja leider nur Referenzen.

Ich könnte sicherlich jetzt jedes Objekt des Arrays auslesen und die Attribute manuell kopieren, aber das finde ich irgendwie nicht schön. Gibts da nicht was besseres?

][immy
2006-03-09, 21:58:22
Hi,

Ich würde gerne ein Array von Objekten komplett (!) kopieren, also nicht nur die Referenzen sondern es sollen neue Objekte mit dem gleichen Inhalt sein.

System.arrayCopy() kopiert ja leider nur Referenzen.

Ich könnte sicherlich jetzt jedes Objekt des Arrays auslesen und die Attribute manuell kopieren, aber das finde ich irgendwie nicht schön. Gibts da nicht was besseres?

wenn deine objekte eine clone-methode besitzen dann nutze diese, ansonsten geht es ansich nur über umwege.

z.B. serialisieren und wieder deserialisieren (funktioniert aber nur wenn die objekte serialisierbar sind)

gleiches problem hatte ich vor kurzem in c#, aber bisher habe ich keine wirklich sinnvolle möglichkeit gefunden

Senior Sanchez
2006-03-09, 22:16:22
[immy']wenn deine objekte eine clone-methode besitzen dann nutze diese, ansonsten geht es ansich nur über umwege.

z.B. serialisieren und wieder deserialisieren (funktioniert aber nur wenn die objekte serialisierbar sind)

gleiches problem hatte ich vor kurzem in c#, aber bisher habe ich keine wirklich sinnvolle möglichkeit gefunden

Hmm, genau diese Varianten habe ich mir auch schon durchdacht.

Mir ist aber aufgefallen, dass ich wohl eh das ganze "von Hand" machen muss, da manche Objekte nicht komplett tief kopiert werden sollen, sondern nur teilweise.

Xmas
2006-03-09, 22:27:24
Es bleibt dir wohl kaum was anderes übrig als für alle Klassen die Kopierbar sein sollen clone(), einen Copy-Konstruktor oder etwas ähnliches zu implementieren.
Viele Objekte lassen sich nun mal nicht "einfach so" kopieren.

Senior Sanchez
2006-03-09, 22:52:25
Es bleibt dir wohl kaum was anderes übrig als für alle Klassen die Kopierbar sein sollen clone(), einen Copy-Konstruktor oder etwas ähnliches zu implementieren.
Viele Objekte lassen sich nun mal nicht "einfach so" kopieren.

Naja, aber es hätte ja sein können das es ne Methode gibt die sämtliche Objekte kopieren kann, notfalls per Reflection.

Monger
2006-03-09, 22:55:55
Wie du selber schon richtig erkannt hast, kann man keine "Kopien" von Objekten machen, weil unklar ist was eine Kopie eigentlich ist. Das würde auch dem Prinzip der Objektorientiertheit entsprechen, denn es gibt niemals zwei identische Objekte, sondern allenfalls ähnliche Objekte.

#clone() ist nicht typsicher, und du weißt nie ob es überladen wurde oder nicht. Deshalb würde ich dir vorschlagen, für Objekte die du kopieren willst, einen passenden Konstruktor zu schreiben. Zum Beispiel:


class Thingy{

public Thingy(Thingy thing){

// hier definieren, was neu erstellt und was referenziert werden soll
}

}

Im JDK wird das oftmals genauso gemacht. Das ist typsicher, und jeder weiß was damit gemeint ist. Damit wird auch klar: eine Kopie ist nichts anderes als ein Objekt, was neu erzeugt wurde und mit den Informationen eines anderen gefüllt wurde. Zwei Objekte sind aber niemals das selbe sondern allenfalls sich ähnlich, sonst wäre es nur ein Objekt.

Xmas
2006-03-09, 23:30:40
Naja, aber es hätte ja sein können das es ne Methode gibt die sämtliche Objekte kopieren kann, notfalls per Reflection.
Das wäre keine gute Idee, weil einige Objekte eben nicht kopiert werden können/dürfen.

Senior Sanchez
2006-03-12, 19:25:34
Das wäre keine gute Idee, weil einige Objekte eben nicht kopiert werden können/dürfen.

Auch wieder wahr.

Danke Monger, du hast ne gute Idee geliefert auf die ich auch hätte selbst kommen können ;) Copy-Constructor, gibts ja auch im schönen C++..... in diesem Fall nur per Hand gebastelt.
Aber so werde ich es machen, dankeschön.

PH4Real
2006-03-13, 15:24:19
Wie du selber schon richtig erkannt hast, kann man keine "Kopien" von Objekten machen, weil unklar ist was eine Kopie eigentlich ist. Das würde auch dem Prinzip der Objektorientiertheit entsprechen, denn es gibt niemals zwei identische Objekte, sondern allenfalls ähnliche Objekte.

#clone() ist nicht typsicher, und du weißt nie ob es überladen wurde oder nicht. Deshalb würde ich dir vorschlagen, für Objekte die du kopieren willst, einen passenden Konstruktor zu schreiben. Zum Beispiel:


class Thingy{

public Thingy(Thingy thing){

// hier definieren, was neu erstellt und was referenziert werden soll
}

}

[...]


Sicherlich eine Methode, die funktioniert :). Aber dank Java 5 sollte es nun auch durch Überschreiben der clone() Methode, ohne der oben genannten Nachteile, funktionieren, da es nun möglich ist den Returntyp von geerbten Funktionen einzuschränken, sowie mittels Annotations das Überschreiben abzusichern.

Also sowas in der Art:

class Thingy implements Clonable {

@Override
public Thingy clone() {
// hier jetzt das "neue" Objekt erstellen...
}

}

Senior Sanchez
2006-03-13, 16:38:55
Wofür sorgt die Annotation @Override?

Ich denke, ich werde trotzdem die Variante von Monger beibehalten, da es so ganz gut zu funktionieren scheint (bis jetzt zumindest).
Welchen Vorteil hat die Variante über clone()?

HellHorse
2006-03-13, 17:30:05
Wofür sorgt die Annotation @Override?
Gar nichts. Ausser wenn die Methode eine andere nicht overridet, was dir jede IDE, die diesen Namen verdient anzeigt, wirft der Compiler eine Warnung oder Error. Keine Ahnung was die Leute bei Sun da geraucht haben, als sie sich das aussdachten.
Siehe: http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Override.html

Monger
2006-03-13, 17:32:32
Wofür sorgt die Annotation @Override?

Sie markiert, dass diese Methode überladen wurde. Afaik ist es einzig und allein ein Hinweis für Javadoc, an der Funktionalität ändert es nix.


Sicherlich eine Methode, die funktioniert . Aber dank Java 5 sollte es nun auch durch Überschreiben der clone() Methode, ohne der oben genannten Nachteile, funktionieren, da es nun möglich ist den Returntyp von geerbten Funktionen einzuschränken, sowie mittels Annotations das Überschreiben abzusichern.

Dein Beispiel ist nicht richtig. Korrekt müsste es heißen:

class Thingy implements Clonable<Thingy> {
@Override
public Thingy clone() {
// hier jetzt das "neue" Objekt erstellen...
}
}


Ich bin ansonsten ein ganz großer Fan der Generics, aber in dem Fall finde ich es hässlich. Aber es sprechen auch andere Dinge gegen #clone().
Clone wurde im JDK ganz mies umgesetzt. Längst nicht jedes Objekt implementiert es, und wenn doch, weiß man nicht wie. In aller Regel ist die Methode auch final, weil man sonst Probleme bei der Vererbung gekriegt hätte: wird jetzt wirklich dieses Objekt geklont, oder doch nur die Oberklasse?

Es ist wirklich hässlich, lass es lieber.

Senior Sanchez
2006-03-13, 18:09:12
Gar nichts. Ausser wenn die Methode eine andere nicht overridet, was dir jede IDE, die diesen Namen verdient anzeigt, wirft der Compiler eine Warnung oder Error. Keine Ahnung was die Leute bei Sun da geraucht haben, als sie sich das aussdachten.
Siehe: http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Override.html

Das habe ich mir auch schon so gedacht, erinnert ja z.B. an @deprecated und das wird ja höchstens vom Compiler geprüft.

Insofern ist die Variante auch wieder blöde und das was Monger sagte, dass man nicht weiß, wie clone nun implementiert ist, zeigt mir, das die von ihm vorgeschlagene Variante wohl die bessere ist.

PH4Real
2006-03-13, 18:58:29
Sie markiert, dass diese Methode überladen wurde. Afaik ist es einzig und allein ein Hinweis für Javadoc, an der Funktionalität ändert es nix.

Nein, nicht für Javadoc, sondern für den Compiler. Das ist schon ein Unterschied.


Dein Beispiel ist nicht richtig. Korrekt müsste es heißen:

class Thingy implements Clonable<Thingy> {
@Override
public Thingy clone() {
// hier jetzt das "neue" Objekt erstellen...
}
}


Ich bin ansonsten ein ganz großer Fan der Generics, aber in dem Fall finde ich es hässlich. Aber es sprechen auch andere Dinge gegen #clone().
Clone wurde im JDK ganz mies umgesetzt. Längst nicht jedes Objekt implementiert es, und wenn doch, weiß man nicht wie. In aller Regel ist die Methode auch final, weil man sonst Probleme bei der Vererbung gekriegt hätte: wird jetzt wirklich dieses Objekt geklont, oder doch nur die Oberklasse?

Es ist wirklich hässlich, lass es lieber.

Leider ist dein Beispiel falsch. ;).

1. Clonable wurde nicht generiziert (was ja auch sinnlos wäre, da das Interface keine Methode enthält). Siehe auch http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Cloneable.html. Deswegen ist dein Beispiel auch gar nicht kompilierbar.

2. Dein Beispiel könnte man auf Callable<T> oder Comparable<T> anwenden, aber das ist was anders.

3. In Java 5 wurden covariant returns eingeführt (worauf sich mein Beispiel bezieht; der Return Typ wird überladen), was jedoch wiederrum nichts mit Generics zu tun hat.

Zu den Problem an sich: Ich würde auch nicht direkt die clone() Methode verwenden, ich wollte jedoch zeigen, dass dies durchaus möglich ist. Ich würde eine eigene Methode anlegen ("deepCopy" oder so), die dann den Konstruktor so aufruft, wie Monger es beschrieben hat (also "return new Thingy(this)", dabei aber den Konstruktor "private" lassen.

Der Vorteil daran ist, dass Du die Methode "deepCopy" in ein Interface oder abstrakte Klasse packen könntest.

Monger
2006-03-13, 20:14:22
Ups, mein Fehler. Erst hatte ich nicht die kovarianten Typen im Kopf (hab sie ehrlich gesagt auch noch nie verwendet), dann habe ich mich auf mein Gedächtnis verlassen und nicht mehr im JDK nachgeschaut.

Wie gesagt, sorry...

HellHorse
2006-03-13, 22:23:01
Das habe ich mir auch schon so gedacht, erinnert ja z.B. an @deprecated und das wird ja höchstens vom Compiler geprüft.
@Deprecated macht aber im Gegensatz zu @Override insofern Sinn, als dass der Compiler nun nicht mehr den Kommentar anschauen muss (!!!!). Oder nicht mehr müsste falls man die Kompatibilität aufgeben würde.

3. In Java 5 wurden covariant returns eingeführt (worauf sich mein Beispiel bezieht; der Return Typ wird überladen), was jedoch wiederrum nichts mit Generics zu tun hat.
Doch eigentlich schon. Den für generics hat man eigens ein Kakül (featherweight Java) geschaffen um sie zu überprüfen. Als man damit das bestehende Java Typsystem modellierte fand man heraus, dass kovariante Rückgabetypen und kontravariante Argumenttypen fehlen. Sun hängt das aber nicht an die grosse Glocke. ;)