Compreendendo este aviso: a classe serializável não declara um serialversion final estático

StackOverflow https://stackoverflow.com/questions/1514881

Pergunta

Eu tenho algum código inicializador estático:

someMethodThatTakesAHashMap(new HashMap<K, V>() {
{
  put("a","value-a"); 
  put("c","value-c");}
});

Por alguma razão, estou recebendo um aviso do Eclipse: a classe serializável não declara um serialversion final estático.

Isso está reclamando da aula anônima? O que posso fazer sobre isso, ou devo apenas suprimi -lo.

Foi útil?

Solução

A sintaxe que você está usando é chamada Inicialização de duas brace - que é realmente um "Bloco de inicialização da instância isso faz parte de um Classe interna anônima"(Certamente não é um hack). Então, ao usar essa notação, você está realmente definindo uma nova classe (!).

O "problema" no seu caso é que HashMap implementos Serializable. Esta interface não tem nenhum método e Serve apenas para identificar a semântica de ser serializável. Em outras palavras, é uma interface marcadora e você concretamente não precisa implementar nada. Mas, durante a desserialização, Java usa um número de versão chamado A serialVersionUID Para verificar se a versão serializada é compatível com o destino. Se você não fornecer isso serialVersionUID, será calculado. E, conforme documentado no Javadoc de Serializable, o valor calculado é extremamente sensível e, portanto, é recomendável que seja explicitamente declarar para evitar problemas de desserialização. E é disso que o Eclipse está "reclamando" sobre (observe que isso é apenas um aviso).

Então, para evitar esse aviso, você pode adicionar um serialVersionUID para sua classe interna anônima:

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

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

Mas você perde a concisão da sintaxe (e pode nem precisar).

Outra opção seria, portanto, ignorar o aviso adicionando um @SuppressWarnings("serial") para o método em que você está ligando someMethodThatTakesAHashMap(Map). Isso parece mais apropriado no seu caso.

Tudo dito, embora essa sintaxe seja concisa, ela tem algumas desvantagens. Primeiro, se você manter uma referência no objeto inicializado usando uma inicialização de duas brace, você implicitamente manterá uma referência ao objeto externo que não será elegível para a coleta de lixo. Por isso tem cuidado. Segundo (isso soa como micro otimização), a inicialização de duas brace tem um Muito um pouco de sobrecarga. Terceiro, essa técnica realmente usa aulas internas anônimas como vimos e, assim, come um pouco de espaço permgen (mas duvido que isso seja realmente um problema, a menos que você verdade abusar deles). Finalmente - e esse é talvez o ponto mais importante - não tenho certeza de que o código seja mais legível (não é uma sintaxe bem conhecida).

Então, enquanto eu gosto de usá -lo em testes (para a concisão), tendem a evitar usá -lo no código "regular".

Outras dicas

Sim, você pode suprimir o aviso, mas eu o reescreveria assim:

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

Nenhuma supressão necessária e muito melhor para ler, IMO.

Eu geralmente concordo com Bart K., mas para fins informativos:
O aviso também pode ser eliminado adicionando o campo, que pode ser gerado automaticamente, atingindo o Ctrl+1.
O aviso também pode ser suprimido adicionando a anotação @suppresswarnings ("serial") antes da definição.
A classe anônima implementa serializável e serializável requer esse campo estático para que as versões possam ser distinguidas ao serializar e desarrializar. Mais informações aqui:
http://www.javablogging.com/what-is-serialversionuid/

o ImmutableMap A classe da biblioteca do Google Collections é útil para esta situação. por exemplo

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

ou

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

Para abordar a outra metade da sua pergunta: "Devo suprimi -la?" -

Sim. Na minha opinião, este é um aviso terrível. SerialversionUid deve, por padrão não ser usado, não o contrário.

Se você não adicionar serialversionUid, a pior coisa que acontece é que duas versões de um objeto que são realmente compatíveis com serialização são consideradas incompatíveis. SerialversionUid é uma maneira de declarar que a compatibilidade de serialização não mudou, substituindo a avaliação padrão do Java.

Usando o SerialVersionUid, a pior coisa que acontece é que você inadvertidamente falha ao atualizar o ID quando o formulário serializado da classe muda de maneira incompatível. Na melhor das hipóteses, você também recebe um erro de tempo de execução. Na pior das hipóteses, algo pior acontece. E imagine como é fácil falhar em atualizá -lo.

Sua intenção era inicializar uma instância anônima de hashmap. O aviso é a pista de que seu código está fazendo mais do que você pretendia.

O que estamos procurando é uma maneira de inicializar uma instância anônima de hashmap. O que temos acima cria uma subclasse anônima de hashmap, então cria uma instância anônima dessa classe anônima.

Como o código faz mais do que o pretendido, eu chamaria isso de hack.

O que realmente queremos é algo assim:

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

Mas, infelizmente, isso não é válido Java. Não há uma maneira de fazer nada isso de uma maneira segura por tipo usando uma matriz de pares de chave/valor. Java simples não tem poder expressivo.

O imutableMapMap da Google Collection. De métodos estáticos estão próximos, mas significa criar uma versão do método de fábrica para vários números de pares de chave/valor. (Veja a resposta de Finnw.)

Portanto, mantenha as coisas simples. Vá com a solução de Bart K, a menos que seu código esteja repleto de inicialização. Se assim for, use mapa imutablel. Ou role sua própria subclasse de hashmap com os métodos de fábrica de estilo "dos". Ou crie esses métodos de fábrica de estilo "em uma classe de utilitário. Aqui está um para dois pares de chave/valor:

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;
    }
}

Abrace a verbosidade e faça consolo no conhecimento que seus colegas corporativos estão usando os mesmos grilhões que você.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top