Wie geht ihr mit der serialVersionUID und Serializable um?

Hallo zusammen,

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 :smiley:

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.

Wie seht/macht ihr das?

Nun, die Doku sagt es deutlich:

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 :wink: )

[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 :wink:
Das Problem ist ja, dass man dann ignoriert weas da sonst noch so steht :wink:
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.

Dem kann ich nur zustimmen :slight_smile:

[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.

Moin,

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.

Viele GrĂŒĂŸe
Fancy

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