hatte letztens eine Diskussion bezueglich serialVersionUID bei Serializable Klassen, wie zB. Entites bei JPA/EclipseLink/Hibernate.
Dabei stellte sich heraus, dass die Meinung darueber schon an der verwendeten IDE vorhersagbar war
Da Eclipse das fehlen der serialVersionUID bei Serializable Klassen als Warnung moniert, waren die Eclipse Entwickler stets der Meinung dass die Warnung gerechtfertigt sei und haben eine serialVersionUID generiert…
Leute die IntelliJ verwendeten sahen das anders, serialVersionUID wurde nur vergeben, wenn man wirklich absichtlich zwischen verschiedenen Versionen serialisieren/deserialisieren wollte, da wurden dann aber auch readObject und writeObject implementiert.
Ich sehe als Eclipse Nutzer aber wie die dortigen IntelliJ User, die Warnung von Eclipse ist fuer mich schlicht ein Fehler der Eclipse Default Config und wird bei jeder neuen Eclipse Installation/WS auf „Ignore“ gesetzt.
However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values
(Und das „strongly recommended“ ist sogar kursiv!!!111elfelfausrufezeichen :eek: )
Tatsächlich frage ich mich auch, spätestens bei irgendwelchen anonymen inneren Klassen, die zufällig von JComponent erben, aber nicht nach außen sichtbar sind (und auch nicht Teil eines Zustandes sind, der einmal serialisiert werden könnte), ob man die nicht einfach weglassen könnte. Und meistens ist die Antwort: Ja, das könnte man. Man kann aber auch einfach „Generate…“ auswählen und damit auf der sicheren Seite sein. (Die Tatsache, dass man so eine Klasse tendenziell ändern würde, ohne die UID neu zu generieren, blende ich jetzt mal ganz bewußt aus ;))
die praktischen Auswirkungen sind doch relativ klar überschaubar, soweit ich sie bei nur seltenen Berühren erinnere:
ohne Id bekommt man Exception bei jeder Änderung, schon bei jedem unterschiedlichen Kompilat desselben Sourcecodes,
also bei jeder Programmversion in allen Klassen,
finde ich normal richtig, was nicht zusammengehört soll man auch merken
mit Id weiß ich nicht recht was im Detail bei Änderungen passiert, zu befürchten ist Ignorieren von Unterschieden/ nicht belegten Feldern?
(doch nicht so klar überschaubar…)
ich kann mir letzteres als keinen Vorteil vorstellen (außer für extrem durchdachte einzelne Klassen, notwendige Übernahme von Informationen an Folgeversionen)
und halte es dagegen (als allgemeinen Standard) für (relativ) brandgefährlich,
überraschend gut vergleichbar mit Überschreiben von hashCode/ equals und pauschale Rückgabe 0/ true, um es ja implementiert zu haben…
auch da gilt: idealerweise nix machen und im Standard nur ein und dasselbe Objekt ist mit sich selber im Reinen,
eine gute Ausgangsbasis!
-> keine serialVersionUID, Warnung ausschalten oder notfalls Suppress überall
Ich schalte die Warnung aus. Für mich ist der Anwendungsfall “Serialisierung” weit weniger wichtig, als übersichtlichen Code zu haben. Und den hat man, je weniger in der Klasse drin steht, inkl. serialVersionUID. Und wenn ichs mal doch brauchen sollte, wird es eben nachgerüstet. Aber pro Forma alle Klassen damit zuzukleistern, obwohl man das Feature eigentlich gar nicht nutzt, ist mir zu viel des Guten ;).
Also ich halte mich an die Doku. Das bedeutet, ich deklariere eine solche SVUID und ändere diese nur, wenn sich an den zu serialisierenden Daten der Klasse etwas ändert. Ferner versuche ich es auch zu vermeiden, Serializables anonym oder als innere Klasse zu verwenden, es sei denn, ich bin mir sicher, dass diese Klasse niemals serialisiert wird, z.B. wenn die umschliessende Klasse selber auch nicht Serializable ist und/oder die Klasse ihre Umgebung nicht verlässt (eine solche Klasse sollte zumindest 1L deklarieren).
Dabei kommt es ja gar nicht so drauf an, ob man dieses Feature selber nutzt, sondern eher darauf, dass andere es nutzen könnten, die meine API nutzen.
Dass man bei Serializable auch viel falsch machen kann, zeigen z.B. Dalvik und Oracle bei der Implementation der Klasse BigInteger, wo beide die selbe SVUID jedoch verschiedene Streaminhalte haben.
Sicher kann man die Warnung schlicht abschalten, aber das ist meines Erachtens so ein “Nach mir die Sintflut”-Ding.
sehe ich wie gesagt gerade andersrum: nicht unterstützen ist die normale Vorgehensweise,
da bekommt der Verwender im Zweifelsfall (unterschiedliche Versionen usw.) eine Exception und das ist genau richtig,
pauschal ohne Nachdenken mit einer gegebenen Id alles freigeben, ohne dann konkret auf Versionsänderungen usw. zu achten,
den Anwender in Sicherheit zu wiegen obwohl gar nichts vorliegt, das ist die Sintflut!
ideal wäre natürlich, gar nicht erst Serializable zu implementieren, von selber kommt man da auch nicht drauf,
mit Swing-GUI oder Hibernate-Daten-Klassen teils leider vorgegeben
*** Edit ***
[quote=Spacerat;92835]ich deklariere eine solche SVUID und ändere diese nur, wenn sich an den zu serialisierenden Daten der Klasse etwas ändert
[…]es sei denn, ich bin mir sicher, dass diese Klasse niemals serialisiert wird[/quote]
bei solchen Punkten kann man nur auf Sorgfalt und Erinnerungsvermögen des Programmiers hoffen um mögliche GAU im Programm zu vermeiden,
wenn dann gar die Programmierer wechseln…
serialVersionUID wegzulassen ist denkbar einfache und konzeptionell lückenlose Lösung
(ich neige mal wieder zur Übertreibung um Gegenargumente zu provozieren, bin gar nicht endgültig überzeugt )
[QUOTE=SlaterB]sehe ich wie gesagt gerade andersrum: nicht unterstützen ist die normale Vorgehensweise[/QUOTE]Klar… Serializable nicht unterstützen…
Sieh mal, da hat sich Sun damals etwas einfallen lassen, was eigentlich gar nicht mal so verkehrt ist, zumindest dann nicht, wenn man es richtig nutzt. Jeder Hans und Franz kann nun daher kommen und sagen, Serializable wäre eine Krücke in Sachen Objektserialisierung und das nur, weil er sich nicht 100%ig an die Empfehlungen hält (für andere Dinge gilt Selbiges oder zumindest Ähnliches). Die Konsequenz daraus aber wäre, selbst einen solchen Mechanismus zu implementieren, wenn benötigt und damit verbunden, die Anwendungen, die diesen nutzen mit einer weiteren Lib, die evtl. an einer weiteren Lizenz hängt, zu belasten. Nur weil es jeder anders macht, verkümmern APIs zu Krücken, die keiner braucht. Aber wenn sich jeder an Empfehlungen halten würde (und wir reden hier nicht von Namens- oder Code-Konventionen ;)), gäbe es keine Probleme. Ist wie mit der Sache bei Swing… wieso sollte ich meine GUI in einem eigenen Thread (“invokeLater()”) aufbauen, wenn es anders auch funktioniert?
Die Entscheidung bei Serializable fällt einem um so leichter, wenn man eindeutig für sich geklärt hat, was eine MagicNumber ist und was es mit einer solchen auf sich hat. Ist zwar nicht wirklich das selbe, aber auf jeden Fall ähnlich.
‘nicht unterstützen’ mag falsche Wortwahl gewesen sein, wobei ich den Gedankengang zu anderen Mechanismen, verkümmern APIs usw. nicht nachvollziehen kann,
schließlich funktioniert ja die Serialisierung ‘mit ohne’ Id,
wäre eher die Frage ob man überhaupt das Interface Serializable anbietet, aber auch bisschen drastisch das für wer weiß wieviel in allen APIs zu verlangen…
hier geht es um ‘mit ohne’ und die Serializierung funktioniert damit wie gesagt nicht nur ein bisschen, sondern ideal, besser als mit Id! :
was genau ändert sich, wenn man die Id (gedankenlos überall) setzt, einmal mehr wiederholt?
-> Klassen unterschiedlicher Version sind kompatibel, womöglich ohne Kontrolle auf inhaltliche Gleichheit, wie kann man das wollen?
eine (theoretisch gesehen…) immense Gefahr und im sowieso seltenen Fall der Serialisierung normalerweise nicht benötigt
nur maximal Klassen genau eines Jars, einer API, sollten mit sich selber kompatibel sein, das ist doch ein zufriedenstellender funktionaler Zustand,
insbesondere hochsicher,
und ohne jeden Aufwand zu erreichen, warum den mit gesetzten Ids gefährden?
wenn speziell benötigt, kontrolliert, dann natürlich gerne, der Inhalt einer Datenbank sollte nicht zur nächsten Version kaputt sein…,
aber nicht als Standard in beliebigen Klassen
Die Doku sagt noch mehr zu diesem Thema
Das Problem ist ja, dass man dann ignoriert weas da sonst noch so steht
Wenn man nicht verschiedene Compiler verwenden will, kann man diese Empfehlung gestrost ignorieren…
Mal so zwei Fragen in den thread geworfen:
Haende hoch, wer alles will verschiedene Compiler verwenden und dann zwischen diesen Compilierten Versionen De-Serialisieren??
Wer will absichtlich zwischen verschiedenen Versionen der SW De-Serialisieren?
Die letztere Frage dagegen stellt den Grund dar warum man ueberhaupt serialVersionUID vergibt, aber nur die serialVersionUID reicht dafuer noch nicht, readObject und writeObject gehoeren auch dazu und ein paar Gedanklen zur kompatiblitaet zwischen Versionen der Objekte…
Ich fuerchte dass genau dieser letzte Schritt von vielen nicht wahrgenommen wird, von den anderen oft einfach nur vergessen… und da liegt einer der Knackpunkte IMHO.
[quote=SlaterB;92814]ohne Id bekommt man Exception bei jeder Änderung, schon bei jedem unterschiedlichen Kompilat desselben Sourcecodes,
also bei jeder Programmversion in allen Klassen,
finde ich normal richtig, was nicht zusammengehört soll man auch merken[/quote]
Eben, „fast fail“ verhalten, wenn etwas nicht passt bekomme ich gleich die passsende Fehlermeldung anstatt impliziten fehlverhaltens.
[QUOTE=maki]Ich fuerchte dass genau dieser letzte Schritt von vielen nicht wahrgenommen wird, von den anderen oft einfach nur vergessen… und da liegt einer der Knackpunkte IMHO.[/QUOTE]Eben, das ist es… Warst du es nicht, der damals im JFO nachgefragt hat, warum BigInteger in Dalvik- und Oracle-VMs nicht kompatibel sind? Sie wären es gewesen, wenn man in der Dalvik-VM nicht den Fehler gemacht hätte, BigInteger anders zu serialisieren. Die Frage nach dem “Wer will freiwillig über verschiedene Versionen serialisieren” stellt sich eigentlich gar nicht, wenn man die restlichen Empfehlungen über Serializable in der Doku auch noch beherzigt (was Dalvik halt nicht getan hat). Es funktioniert damit halt über verschiedene Versionen hinweg. Ebenso, wie man z.B. JPEGS in verschiedene Zeichenprogramme laden kann. Es ist halt nicht nur wichtig, dass ein Serializable eine SVUID deklariert, sondern auch dass diese SVUID zum Klassennamen und zum erzeugten Stream passt. Wozu verwendet man den Serializable? Sicher nicht dazu, um Daten auf Festplatte zu speichern (ok, das tun viele zwar auch), sondern Objekte durchs Netzwerk schicken zu können und in einem solchen weis man nie welche Version die Gegenseite fährt.
Demnach bin ich der Meinung, dass “die SVUID einfach weglassen” nur deswegen die einfachste und beste Möglichkeit sein mag, weil evtl. inzwischen so ziemlich jeder das so sieht. Deswegen gibt es ja auch soviele Ersatzmechanismen dafür, die genau dieselben Krankheiten haben. Ein serialisierter BigInteger bleibt ein serialisierter BigInteger (sollte zumindest), ebenso wie ein JPEG ein JPEG bleibt. Anders macht Serializable keinen Sinn und dann kann man es auch gänzlich ignorieren.
BTW.: Ein korrekt serialisiertes Objekt sollte eigentlich beim deserialisieren keine Fehler ergeben, sondern funktionieren und das geht versionsübergreifend nur mit SVUID.
ich verwende @SuppressWarnings(“serial”). Das hat für mich zwei Vorteile:
Eclipse “zwingt” mich in jedem konkreten Fall dazu (kurz) darüber nachzudenken, was ich eigentlich möchte. Und ich dokumentiere meine Entscheidung im Quellcode dann eben mit @SuppressWarnings(“serial”) oder (selten) mit einer serialVersionUID.
Wenn jemand anders das Projekt mit seiner IDE öffnet, wird er nicht mit unnötigen Warnings zugespammt.
BigInteger steht in der System-Library, welche quasi unvermeidbar in verschiedensten Versionen vorliegt,
da hat man keine Kontrolle, muss natürlich vertrauen dass das versionsübergreifend funktioniert
alle derartigen System-Klassen stehen außer Frage
Jpeg als Java-unabhängiges Speicherformat passt nicht ganz,
aber entsprechend für Java überlegt können wichtige langfristige Datencontainer natürlich Serialisierung mit allen Drum und Dran,
Versionskontrolle, Verarbeitungshinweise, Ergänzung von fehlenden Daten usw. Serialisierung an die Spitze treiben
ein lesbares, programmiersprachenunabhängiges Format sei aber angedacht,
nicht umsonst gibt es XML, HTML, JPEG eben, IP/TCP, Webservice , bestimmt auch OpenSSL-Protokolle, usw.,
man weiß nicht welche Version die Gegenseite hat, aber auch nicht unbedingt ob dort Java läuft,
wenn man schon über Versionen nachdenkt wieso sich dann noch auf Java beschränken?
es sei denn es geht eben um z.B. RMI, breite Kommunikation mit zig Java-Klassen, keine einzelnen dicken Daten-Formate
selbst für internationale Libraries wie Commons ist es durchaus möglich und abgebracht, mit derselben Version zu arbeiten, ‚mit ohne‘ bereits denkbar,
spätestens bei eigenen Klassen/ Libraries, die wohl selten einen ähnlichen Status erreichen, kann man bestimmte Version überall leicht voraussetzen
wenn man von den konkreten aktiv bearbeiteten Daten-Klassen hin zu unbeachteten Allerwelts-Hilfsklassen in der eigenen Library übergeht, dem Thema hier,
wenn etwa bei RMI ohne richtige User-Informierung alles mögliche übertragen wird,
wird es umso zwingender, ‚mit ohne‘ zu arbeiten, auf gleiche Version der Library überall zu pochen
(man kann es ja auch erstmal so probieren, spätestens im Fehlerfall neue Version nötig)
überall ungesehen ungeprüft eine Id gesetzt (ich weiß, davon schreibst du @Spacerat bisher nichts explizit, auf Thema an sich bezogen)
bedeutet Verzicht auf Kontrolle, reines Vertrauen dass alles sauber programmiert wurde
bei Klassen die gar nicht übertragen werden ist es egal, aber dann bringt die Dummy-Id 1 auch keinen Vorteil, dann kann man ebenso drauf verzichten