理解此警告:可序列化类不声明静态最终serialVersionUID

我有一些静态初始化代码:

someMethodThatTakesAHashMap(new HashMap() { { put("a","value-a"); put("c","value-c");} }); 

出于某种原因,我收到Eclipse的警告:serializable类没有声明静态的最终serialVersionUID。

这是抱怨匿名课吗? 我能做些什么,或者我应该压制它。

您正在使用的语法称为双括号初始化 – 实际上是“ 实例初始化块 ,它是匿名内部类的一部分 ”(当然不是黑客)。 因此,在使用这种表示法时,您实际上是在定义一个新类(!)。

你的案例中的“问题”是HashMap实现了Serializable 。 此接口没有任何方法, 仅用于标识可序列化的语义 。 换句话说,它是一个标记界面,你具体地不需要实现任何东西。 但是 ,在反序列化期间,Java使用名为serialVersionUID的版本号来validation序列化版本是否与目标兼容。 如果您不提供此serialVersionUID ,则会计算它。 并且,正如Serializable的javadoc中所记录的那样,计算出的值非常敏感,因此建议明确声明它以避免任何反序列化问题。 这就是Eclipse“抱怨”的原因(注意这只是一个警告)。

因此,要避免此警告,您可以将serialVersionUID添加到您的匿名内部类:

 someMethodThatTakesAHashMap(new HashMap() { private static final long serialVersionUID = -1113582265865921787L; { put("a", "value-a"); put("c", "value-c"); } }); 

但是你松开了语法的简洁性(你可能甚至不需要它)。

因此,另一种选择是通过向您调用someMethodThatTakesAHashMap(Map)的方法添加@SuppressWarnings("serial")来忽略警告。 在您的情况下,这似乎更合适。

尽管如此,虽然这种语法简洁,但它有一些缺点。 首先,如果对使用双括号初始化初始化的对象持有引用,则隐式保存对外部对象的引用,该引用不符合垃圾回收的条件。 所以要小心。 第二(这听起来像微优化),双支撑初始化有一点点开销 。 第三,这种技术实际上使用了我们看到的匿名内部类,因此吃了一点permgen空间(但我怀疑这确实是一个问题,除非你真的滥用它们)。 最后 – 这可能是最重要的一点 – 我不确定它是否使代码更具可读性(它不是一个众所周知的语法)。

因此,虽然我喜欢在测试中使用它(为了简洁),但我倾向于避免在“常规”代码中使用它。

是的,你可以取消警告,但我会像这样重写它:

 HashMap map = new HashMap(); map.put("a","value-a"); map.put("c","value-c"); someMethodThatTakesAHashMap(map); 

IMO没有需要抑制,也没有更好的阅读。

我普遍同意Bart K.,但出于提供信息的目的:
通过添加字段也可以消除警告,该字段可以通过按ctrl + 1自动生成。
也可以通过在定义之前添加@SuppressWarnings(“serial”)注释来抑制警告。
匿名类实现Serializeable,Serializeable需要此静态字段,以便在序列化和反序列化时可以区分版本。 更多信息:
http://www.javablogging.com/what-is-serialversionuid/

来自Google Collections库的ImmutableMap类对此情况很有用。 例如

 someMethodThatTakesAHashMap(ImmutableMap.builder().put("a","value-a").put("c","value-c").build()); 

要么

 someMethodThatTakesAHashMap(ImmutableMap.of("a","value-a","c","value-c")); 

为了解决你问题的另一半,“我应该压制它吗?” –

是。 在我看来,这是一个可怕的警告。 默认情况下不应使用serialVersionUID,而不是相反。

如果你不添加serialVersionUID,最糟糕的事情是两个版本的实际序列化兼容的对象被认为是不兼容的。 serialVersionUID是一种声明序列化兼容性未发生更改的方法,它覆盖了Java的默认评估。

使用serialVersionUID,最糟糕的事情是,当类的序列化表单以不兼容的方式更改时,您无意中无法更新ID。 最多也会出现运行时错误。 最糟糕的是,会发生更糟糕的事 想象一下,更新它是多么容易。

您的目的是初始化HashMap的匿名实例。 警告提示您的代码比您预期的更多。

我们正在寻找的是一种初始化匿名HashMap实例的方法。 我们上面创建的HashMap的匿名子类然后创建该匿名类的匿名实例。

因为代码比预期更多,我称之为黑客。

我们真正想要的是这样的:

 foo(new HashMap({"a", "value-a"}, {"c", "value-c"})); 

但是这不是有效的Java。 使用键/值对数组以类型安全的方式无法做任何事情。 Java简单没有表现力。

Google Collection的ImmutableMap.of静态方法很接近,但它意味着为不同数量的键/值对创建工厂方法的版本。 (见finnw的回答。)

所以保持简单。 除非你的代码充满了这个初始化,否则请使用Bart K的解决方案。 如果是这样,请使用ImmutableMap。 或者使用“of”样式工厂方法滚动自己的HashMap子类。 或者在实用程序类中创建这些“样式”工厂方法。 这是两个键/值对的一个:

 public final MapUtil { public static  Map makeMap(K k1, V v1, K k2, V v2) { Map m = new HashMap(); m.put(k1, v1); m.put(k2, v2); return m; } } 

拥抱冗长,并在您的公司同事戴着与您相同的枷锁的知识中获得安慰。