No meu arquivo de contexto do aplicativo Spring, tenho algo como:
<util:map id="someMap" map-class="java.util.HashMap" key-type="java.lang.String" value-type="java.lang.String">
<entry key="some_key" value="some value" />
<entry key="some_key_2" value="some value" />
</util:map>
Na classe java, a implementação se parece com:
private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
No Eclipse, vejo um aviso que diz:
Segurança de tipo: elenco não verificado de Object para HashMap
O que eu fiz errado? Como resolvo o problema?
java
spring
type-safety
unchecked
DragonBorn
fonte
fonte
Respostas:
Bem, primeiro de tudo, você está desperdiçando memória com a nova
HashMap
chamada de criação. Sua segunda linha desconsidera completamente a referência a este hashmap criado, disponibilizando-o para o coletor de lixo. Portanto, não faça isso, use:Em segundo lugar, o compilador está reclamando que você lança o objeto para a
HashMap
sem verificar se é aHashMap
. Mas, mesmo se você fizesse:Você provavelmente ainda receberia esse aviso. O problema é que
getBean
retornaObject
, então não se sabe qual é o tipo. Convertê-lo paraHashMap
diretamente não causaria o problema com o segundo caso (e talvez não houvesse um aviso no primeiro caso, não tenho certeza de quão pedante é o compilador Java com avisos para o Java 5). No entanto, você está convertendo para aHashMap<String, String>
.HashMaps são realmente mapas que pegam um objeto como chave e têm um objeto como valor,
HashMap<Object, Object>
se você preferir. Portanto, não há garantia de que, quando você obtiver seu bean, ele possa ser representado comoHashMap<String, String>
porque você poderia ter,HashMap<Date, Calendar>
porque a representação não genérica retornada pode ter qualquer objeto.Se o código for compilado e você puder executar
String value = map.get("thisString");
sem erros, não se preocupe com esse aviso. Mas se o mapa não for completamente de chaves de string para valores de string, você receberá umClassCastException
em tempo de execução, porque os genéricos não podem impedir que isso aconteça nesse caso.fonte
O problema é que um elenco é uma verificação de tempo de execução - mas devido ao tipo de apagamento, em tempo de execução não há realmente nenhuma diferença entre um
HashMap<String,String>
eHashMap<Foo,Bar>
para qualquer outroFoo
eBar
.Use
@SuppressWarnings("unchecked")
e segure seu nariz. Ah, e faça campanha para genéricos reificados em Java :)fonte
Como as mensagens acima indicam, a Lista não pode ser diferenciada entre a
List<Object>
e aList<String>
ouList<Integer>
.Resolvi esta mensagem de erro para um problema semelhante:
com o seguinte:
Explicação: A primeira conversão de tipo verifica se o objeto é uma Lista sem se preocupar com os tipos contidos nela (pois não podemos verificar os tipos internos no nível da Lista). A segunda conversão agora é necessária porque o compilador sabe apenas que a lista contém algum tipo de objeto. Isso verifica o tipo de cada objeto na lista conforme ele é acessado.
fonte
Um aviso é exatamente isso. Um aviso. Às vezes, os avisos são irrelevantes, às vezes não. Eles são usados para chamar sua atenção para algo que o compilador acha que pode ser um problema, mas pode não ser.
No caso de elencos, sempre haverá um aviso neste caso. Se você tem certeza absoluta de que um elenco específico será seguro, considere adicionar uma anotação como esta (não tenho certeza da sintaxe) antes da linha:
fonte
Você está recebendo esta mensagem porque getBean retorna uma referência de objeto e a está transmitindo para o tipo correto. O Java 1.5 fornece um aviso. Essa é a natureza do uso do Java 1.5 ou melhor com código que funciona assim. Spring tem a versão typesafe
na sua lista de tarefas.
fonte
Se você realmente deseja se livrar dos avisos, uma coisa que você pode fazer é criar uma classe que se estenda a partir da classe genérica.
Por exemplo, se você está tentando usar
Você pode criar uma nova classe como essa
Então quando você usa
O compilador sabe quais são os tipos (não mais genéricos) e não haverá aviso. Isso pode nem sempre ser a solução perfeita, alguns podem argumentar que esse tipo de derrota o propósito das classes genéricas, mas você ainda está reutilizando todo o mesmo código da classe genérica, mas está declarando, em tempo de compilação, que tipo você quer usar
fonte
A solução para evitar o aviso desmarcado:
fonte
Outra solução, se você estiver lançando muito o mesmo objeto e não quiser colocar seu código em lixo
@SupressWarnings("unchecked")
, seria criar um método com a anotação. Dessa forma, você centraliza o elenco e, com sorte, reduz a possibilidade de erro.fonte
O código abaixo causa o aviso de segurança de tipo
Map<String, Object> myInput = (Map<String, Object>) myRequest.get();
Crie um novo objeto de mapa sem mencionar os parâmetros, porque o tipo de objeto mantido na lista não é verificado.
Etapa 1: criar um novo mapa temporário
Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();
Etapa 2: instanciar o mapa principal
Etapa 3: Iterar o mapa temporário e definir os valores no mapa principal
fonte
Aqui :
Map<String,String> someMap = (Map<String,String>)getApplicationContext().getBean("someMap");
Você usa um método legado que geralmente não queremos usar, pois retorna
Object
:O método a favor de obter (para singleton) / criar (para protótipo) um bean de uma fábrica de beans é:
Usando-o como:
será compilado, mas ainda com um aviso de conversão desmarcado, pois
Map
nem todos os objetos são necessariamenteMap<String, String>
objetos.Mas
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
não é suficiente nas classes genéricas do bean, como coleções genéricas, pois isso requer especificar mais de uma classe como parâmetro: o tipo de coleção e seu (s) tipo (s) genérico (s).Nesse tipo de cenário e em geral, uma abordagem melhor não é usar
BeanFactory
métodos diretamente , mas deixar a estrutura injetar o bean.A declaração do bean:
A injeção de feijão:
fonte