Java 理解此警告:serializable类不声明静态最终serialVersionUID

Java 理解此警告:serializable类不声明静态最终serialVersionUID,java,generics,instance-initializers,Java,Generics,Instance Initializers,我有一些静态初始化器代码: someMethodThatTakesAHashMap(new HashMap<K, V>() { { put("a","value-a"); put("c","value-c");} }); somethodthatakesahashmap(新的HashMap(){ { 卖出(“a”、“价值a”); put(“c”,“value-c”);} }); 出于某种原因,我收到了来自Eclipse的警告: serializable类未声明静态最终s

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

someMethodThatTakesAHashMap(new HashMap<K, V>() {
{
  put("a","value-a"); 
  put("c","value-c");}
});
somethodthatakesahashmap(新的HashMap(){
{
卖出(“a”、“价值a”);
put(“c”,“value-c”);}
});
出于某种原因,我收到了来自Eclipse的警告: serializable类未声明静态最终serialVersionUID


这是在抱怨匿名类吗?对此我能做些什么,或者我应该抑制它。

是的,你可以抑制警告,但我会这样重写它:

HashMap<String, String> map  = new HashMap<String, String>();
map.put("a","value-a"); 
map.put("c","value-c");
someMethodThatTakesAHashMap(map);
foo(new HashMap<String, String>({"a", "value-a"}, {"c", "value-c"}));
HashMap map=newhashmap();
地图放置(“a”、“a值”);
地图放置(“c”、“价值c”);
采用SAHASHMAP(map)的一些方法;

我不需要压制,而且读起来更好。

我大体上同意巴特·K.的观点,但出于提供信息的目的:
还可以通过添加字段来消除警告,该字段可以通过按ctrl+1自动生成。
也可以通过在定义之前添加@SuppressWarnings(“序列”)注释来抑制警告。
匿名类实现Serializeable,Serializeable需要此静态字段,以便在序列化和反序列化时可以区分版本。更多信息请点击此处:

您使用的语法被称为-实际上是一个“that is a part of a”(当然不是hack)。因此,当使用这种表示法时,实际上是在定义一个新类(!)

您案例中的“问题”是实现。此接口没有任何方法,仅用于标识可序列化的语义。换句话说,它是一个标记接口,您实际上不需要实现任何东西但是在反序列化过程中,Java使用名为
serialVersionUID的版本号来验证序列化版本是否与目标兼容。如果您不提供此
serialVersionUID
,则将对其进行计算。并且,如的javadoc中所述,计算值非常敏感,因此建议显式声明它以避免任何反序列化问题。这就是Eclipse“抱怨”的地方(请注意,这只是一个警告)

因此,为了避免出现此警告,您可以向annonymous内部类添加
serialVersionId

someMethodThatTakesAHashMap(new HashMap<String, String>() {
    private static final long serialVersionUID = -1113582265865921787L;

    {
        put("a", "value-a");
        put("c", "value-c");
    }
});
somethodthatakesahashmap(新的HashMap(){
私有静态最终长serialVersionUID=-1113582265865921787L;
{
卖出(“a”、“价值a”);
看跌期权(“c”、“价值c”);
}
});
但是,您失去了语法的简洁性(甚至可能不需要它)

因此,另一个选项是忽略警告,方法是将
@SuppressWarnings(“serial”)
添加到调用
somethodthattakeshashmap(Map)
的方法中。这似乎更适合你的情况

总而言之,虽然这种语法很简洁,但也有一些缺点。首先,如果您在使用双括号初始化的对象上持有一个引用,那么您将隐式持有一个对外部对象的引用,该外部对象将不符合垃圾收集的条件。所以要小心。其次(这听起来像是微观优化),双大括号初始化有一个简单的过程。第三,正如我们所看到的,这种技术实际上使用了匿名内部类,因此占用了一些permgen空间(但我怀疑这是否真的是一个问题,除非你真的滥用它们)。最后——这可能是最重要的一点——我不确定它是否能使代码更具可读性(这不是一种众所周知的语法)


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

来自Google Collections库的
ImmutableMap
类对于这种情况很有用。e、 g

someMethodThatTakesAHashMap(ImmutableMap.<K, V>builder().put("a","value-a").put("c","value-c").build());

回答你问题的另一半,“我应该抑制它吗?”--

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

如果不添加serialVersionUID,最糟糕的情况是,实际上与序列化兼容的对象的两个版本被视为不兼容。serialVersionUID是一种声明序列化兼容性没有改变的方法,它覆盖了Java的默认评估


使用serialVersionUID,最糟糕的情况是,当类的序列化表单以不兼容的方式更改时,您无意中未能更新ID。充其量,您也会得到一个运行时错误。最坏的情况是,更糟糕的事情发生了。想象一下更新失败是多么容易。

您的意图是初始化HashMap的匿名实例。警告提示您的代码执行的操作超出了预期

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

因为代码做的比预期的要多,我称之为黑客

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

HashMap<String, String> map  = new HashMap<String, String>();
map.put("a","value-a"); 
map.put("c","value-c");
someMethodThatTakesAHashMap(map);
foo(new HashMap<String, String>({"a", "value-a"}, {"c", "value-c"}));
foo(新的HashMap({“a”,“value-a”},{“c”,“value-c”}));
但遗憾的是,这不是有效的Java。没有一种方法可以使用键/值对数组以类型安全的方式执行此操作。Java simple没有表达能力

Google集合的ImmutableMap.of静态方法非常接近,但这意味着为不同数量的键/值对创建一个工厂方法版本。(见finnw的回答。)

所以,事情要简单。使用BartK的解决方案,除非您的代码中充斥着这种初始化。如果是,请使用ImmutableMap。或者使用“of”风格的工厂方法滚动您自己的HashMap子类。或者在实用程序类中创建这些“of”样式的工厂方法。这里有一个用于两个键/值对:

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;
    }
}
public final MapUtil{
公共静态映射makeMap(kk1,vv1,kk2,vv2){
Map m=新的HashMap();
m、 put(k1,v1);
m、 put(k2,v2);
返回m;
}
}
电子制动