この警告を理解する:シリアル化可能なクラスでは、静的な最終シリアルバージョンを宣言しません
-
19-09-2019 - |
質問
静的イニシャルコードがいくつかあります:
someMethodThatTakesAHashMap(new HashMap<K, V>() {
{
put("a","value-a");
put("c","value-c");}
});
何らかの理由で、私はEclipseから警告を受けています。シリアル化可能なクラスは、静的な最終シリアルVersionUidを宣言しません。
これは匿名のクラスについて不平を言っていますか?私はそれについて何ができるか、それともただそれを抑制する必要があります。
解決
使用している構文は呼び出されます ダブルブレースの初期化 - 実際には」インスタンス初期化ブロック それはの一部です 匿名の内部クラス「(確かにハックではありません)。したがって、この表記法を使用する場合、実際に新しいクラスを定義しています(!)。
あなたの場合の「問題」はそれです HashMap
道具 Serializable
. 。このインターフェイスには方法がありません シリアル化可能であるというセマンティクスを特定するためだけにサービスを提供します. 。言い換えれば、それはマーカーインターフェイスであり、具体的には何も実装する必要はありません。 しかし, 、脱出中、Javaはと呼ばれるバージョン番号を使用します serialVersionUID
シリアル化されたバージョンがターゲットと互換性があることを確認します。これを提供しない場合 serialVersionUID
, 、計算されます。そして、Javadocで文書化されているように Serializable
, 、計算された値は非常に敏感であるため、脱出の問題を回避するために明示的に宣言することをお勧めします。そして、これは日食が「不平を言っている」ものです(これは単なる警告であることに注意してください)。
したがって、この警告を避けるために、あなたは serialVersionUID
あなたの匿名の内なるクラスに:
someMethodThatTakesAHashMap(new HashMap<String, String>() {
private static final long serialVersionUID = -1113582265865921787L;
{
put("a", "value-a");
put("c", "value-c");
}
});
しかし、あなたは構文の簡潔さを失います(そして、あなたもそれを必要としないかもしれません)。
したがって、別のオプションは、を追加して警告を無視することです @SuppressWarnings("serial")
あなたが呼んでいる方法に someMethodThatTakesAHashMap(Map)
. 。これはあなたの場合にもっと適しているようです。
この構文は簡潔ですが、いくつかの欠点があります。まず、ダブルブレース初期化を使用して初期化されたオブジェクトの参照を保持する場合、ガベージコレクションの対象となる外部オブジェクトへの参照を暗黙的に保持します。ので注意してください。 2番目(これはミクロの最適化のように聞こえます)、ダブルブレースの初期化には 非常に頭上です. 。第三に、この手法は実際に私たちが見たように匿名の内部クラスを使用しているため、パーマンスペースを少し食べます(しかし、これは本当に問題であるとは思わない 本当 それらを乱用します)。最後に - これが最も重要なポイントかもしれません - それがコードをより読みやすくするかどうかはわかりません(それはよく知られている構文ではありません)。
ですから、私はそれをテストで使用するのが好きですが(簡潔さのために)、「通常の」コードでそれを使用することを避ける傾向があります。
他のヒント
はい、あなたは警告を抑制することができますが、私はそれをこのように書き直します:
HashMap<String, String> map = new HashMap<String, String>();
map.put("a","value-a");
map.put("c","value-c");
someMethodThatTakesAHashMap(map);
抑制は必要ありません、そして読むのがはるかに良いです、IMO。
私は一般的にBart Kに同意しますが、情報提供のために:
警告は、フィールドを追加することで排除することもできます。フィールドは、Ctrl+1にヒットすることで自動的に生成できます。
また、定義の前に @suppresswarnings( "serial")注釈を追加することで、警告を抑制することもできます。
匿名のクラスはシリアル化可能であると実装し、シリアル化可能な場合は、この静的フィールドが必要になるため、シリアル化とシリアル化の際にバージョンを区別できるようにします。詳細はこちら:
http://www.javablogging.com/what-is-serialversionuid/
ImmutableMap
Google Collections Libraryのクラスは、この状況に役立ちます。例えば
someMethodThatTakesAHashMap(ImmutableMap.<K, V>builder().put("a","value-a").put("c","value-c").build());
また
someMethodThatTakesAHashMap(ImmutableMap.of("a","value-a","c","value-c"));
あなたの質問の残りの半分に「私はそれを抑制するべきですか?」に対処するために -
はい。私の意見では、これはひどい警告です。 serialversionuidはデフォルトで必要です いいえ 逆ではなく、使用してください。
SerialVersionUidを追加しない場合、最悪の事態は、実際にシリアル化互換性のあるオブジェクトの2つのバージョンが互換性がないとみなされることです。 SerialVersionUidは、シリアル化の互換性が変更されておらず、Javaのデフォルト評価を最優先していることを宣言する方法です。
SerialVersionUidを使用すると、最悪の事態は、クラスのシリアル化されたフォームが互換性のない方法で変更されたときに不注意にIDを更新できないことです。せいぜい、ランタイムエラーも発生します。最悪の場合、何か悪いことが起こります。そして、それを更新するのがどれほど簡単か想像してみてください。
あなたの意図は、ハッシュマップの匿名インスタンスを初期化することでした。警告は、あなたのコードが意図した以上に行っているという手がかりです。
私たちが探しているのは、匿名のハッシュマップインスタンスを初期化する方法です。上記のものは、Hashmapの匿名のサブクラスを作成し、その匿名クラスの匿名インスタンスを作成します。
コードは意図した以上のことをしているので、私はそれをハックと呼びます。
私たちが本当に望んでいるのは、このようなものです:
foo(new HashMap<String, String>({"a", "value-a"}, {"c", "value-c"}));
しかし、悲しいかな、これは有効なJavaではありません。キー/値のペアの配列を使用して、タイプセーフの方法でこれを行う方法はありません。 Java Simpleには表現力がありません。
Google Collectionの静的メソッドのImmutableMapは近いですが、さまざまな数のキー/バリューペアのファクトリーメソッドのバージョンを作成することを意味します。 (Finnwの回答を参照してください。)
だから、物事をシンプルにしてください。コードにこの初期化が散らばっていない限り、BART Kのソリューションを使用してください。もしそうなら、Immutablemapを使用してください。または、「of」スタイルの工場メソッドを使用して、独自のハッシュマップサブクラスをロールします。または、ユーティリティクラスでこれらの「スタイルファクトリーメソッドを作成します。これが2つのキー/値のペアの1つです:
public final MapUtil {
public static <K,V> Map<K,V> makeMap(K k1, V v1, K k2, V v2) {
Map<K,V> m = new HashMap<K,V>();
m.put(k1, v1);
m.put(k2, v2);
return m;
}
}
冗長性を受け入れ、あなたの企業の同僚があなたと同じシャックルを着ている知識に慰めを取ります。