O Eclipse está me enviando um aviso do seguinte formulário:
Segurança de tipo: elenco não verificado de Object para HashMap
Isso é de uma chamada para uma API que eu não tenho controle sobre o que retorna Object:
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
return theHash;
}
Eu gostaria de evitar avisos do Eclipse, se possível, pois teoricamente eles indicam pelo menos um possível problema de código. Ainda não encontrei uma boa maneira de eliminar esta. Posso extrair a linha única envolvida em um método por si só e adicioná @SuppressWarnings("unchecked")
-lo, limitando assim o impacto de ter um bloco de código no qual ignoro os avisos. Alguma opção melhor? Não quero desativar esses avisos no Eclipse.
Antes de eu entrar no código, era mais simples, mas ainda provocava avisos:
HashMap getItems(javax.servlet.http.HttpSession session) {
HashMap theHash = (HashMap)session.getAttribute("attributeKey");
return theHash;
}
O problema estava em outro lugar quando você tentava usar o hash, recebia avisos:
HashMap items = getItems(session);
items.put("this", "that");
Type safety: The method put(Object, Object) belongs to the raw type HashMap. References to generic type HashMap<K,V> should be parameterized.
enum
ou até mesmo exemplosClass<T>
), para que você possa dar uma olhada e saber que é seguro.Respostas:
A resposta óbvia, é claro, não é fazer o elenco desmarcado.
Se for absolutamente necessário, tente pelo menos limitar o escopo da
@SuppressWarnings
anotação. De acordo com seus Javadocs , ele pode acessar variáveis locais; dessa forma, nem afeta todo o método.Exemplo:
Não há como determinar se o
Map
realmente deve ter os parâmetros genéricos<String, String>
. Você deve saber de antemão quais devem ser os parâmetros (ou descobrirá quando receber umClassCastException
). É por isso que o código gera um aviso, porque o compilador não pode saber se é seguro.fonte
String s = (String) new Object() ;
não recebe aviso, mesmo que o compilador não saiba que o elenco é seguro. O aviso é porque o compilador (a) não sabe que a conversão é segura E (b) não gerará uma verificação completa do tempo de execução no ponto da conversão. Haverá uma verificação de que é umHashmap
, mas não haverá uma verificação de que é umHashMap<String,String>
.Infelizmente, não há ótimas opções aqui. Lembre-se, o objetivo de tudo isso é preservar a segurança do tipo. " Java Generics " oferece uma solução para lidar com bibliotecas herdadas não genéricas, e existe uma em particular chamada "técnica de loop vazio" na seção 8.2. Basicamente, faça o elenco inseguro e suprima o aviso. Em seguida, percorra o mapa da seguinte maneira:
Se um tipo inesperado for encontrado, você obterá um tempo de execução
ClassCastException
, mas pelo menos isso acontecerá próximo à origem do problema.fonte
Uau; Acho que descobri a resposta para minha própria pergunta. Só não tenho certeza se vale a pena! :)
O problema é que o elenco não está marcado. Então, você tem que verificar você mesmo. Você não pode simplesmente verificar um tipo parametrizado com instanceof, porque as informações do tipo parametrizado não estão disponíveis no tempo de execução, pois foram apagadas no tempo de compilação.
Mas, você pode executar uma verificação em todos os itens do hash, com instanceof, e, ao fazer isso, pode construir um novo hash que seja seguro para o tipo. E você não provocará nenhum aviso.
Graças a mmyers e Esko Luontola, parametrizei o código que originalmente escrevi aqui, para que ele possa ser agrupado em uma classe de utilitário em algum lugar e usado para qualquer HashMap parametrizado. Se você deseja entendê-lo melhor e não estiver muito familiarizado com os genéricos, incentive a visualização do histórico de edições desta resposta.
Isso dá muito trabalho, possivelmente com muito pouca recompensa ... Não sei se vou usá-lo ou não. Agradeço qualquer comentário sobre se as pessoas acham que vale a pena ou não. Além disso, eu gostaria de receber sugestões de melhorias: há algo melhor que eu posso fazer além de lançar AssertionErrors? Existe algo melhor que eu poderia jogar? Devo torná-lo uma exceção marcada?
fonte
Nas Preferências do Eclipse, acesse Java-> Compilador-> Erros / Avisos-> Tipos genéricos e marque a
Ignore unavoidable generic type problems
caixa de seleção.Isso satisfaz a intenção da pergunta, ou seja,
se não o espírito.
fonte
uses unchecked or unsafe operations.
" Estava recebendo um errojavac
, mas a adição@SuppressWarnings("unchecked")
deixou o Eclipse infeliz, alegando que a supressão era desnecessária. Desmarcar esta caixa faz com que o Eclipsejavac
se comporte da mesma maneira que eu queria. Suprimir explicitamente o aviso no código é muito mais claro do que suprimi-lo em qualquer lugar dentro do Eclipse.Você pode criar uma classe de utilitário como a seguir e usá-la para suprimir o aviso não verificado.
Você pode usá-lo da seguinte maneira:
Um pouco mais de discussão sobre isso está aqui: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
fonte
vi
? Você está de brincadeira?Isso é difícil, mas aqui estão os meus pensamentos atuais:
Se a sua API retornar Objeto, não haverá nada que você possa fazer - não importa o quê, você estará lançando o objeto às cegas. Você permite que o Java execute ClassCastExceptions ou você mesmo pode verificar cada elemento e lançar Assertions ou IllegalArgumentExceptions ou algo parecido, mas essas verificações em tempo de execução são todas equivalentes. Você precisa suprimir o tempo de compilação não verificado, independentemente do que faz em tempo de execução.
Eu preferiria fazer a conversão às cegas e permitir que a JVM execute sua verificação de tempo de execução para mim, pois "sabemos" o que a API deve retornar e geralmente queremos assumir que a API funciona. Use genéricos em qualquer lugar acima do elenco, se precisar deles. Você realmente não está comprando nada lá, pois ainda possui o elenco de blinds únicos, mas pelo menos pode usar genéricos a partir daí para que a JVM possa ajudá-lo a evitar transmissões de blinds em outras partes do seu código.
Nesse caso em particular, presumivelmente, você pode ver a chamada para SetAttribute e ver o tipo de entrada; portanto, apenas ocultar o tipo para o mesmo na saída não é imoral. Adicione um comentário referenciando o SetAttribute e pronto.
fonte
Aqui está um exemplo abreviado que evita o aviso de "elenco não verificado" empregando duas estratégias mencionadas em outras respostas.
Passe a Classe do tipo de interesse como parâmetro em tempo de execução (
Class<T> inputElementClazz
). Então você pode usar:inputElementClazz.cast(anyObject);
Para conversão de tipo de uma coleção, use o curinga? em vez de um tipo genérico T para reconhecer que você realmente não sabe que tipo de objetos esperar do código legado (
Collection<?> unknownTypeCollection
). Afinal, é isso que o aviso de "elenco não verificado" quer nos dizer: não podemos ter certeza de que recebemos umCollection<T>
, então a coisa mais honesta a fazer é usar aCollection<?>
. Se for absolutamente necessário, uma coleção de um tipo conhecido ainda pode ser construída (Collection<T> knownTypeCollection
).O código legado com interface no exemplo abaixo possui um atributo "input" no StructuredViewer (StructuredViewer é um widget de árvore ou tabela, "input" é o modelo de dados por trás dele). Essa "entrada" pode ser qualquer tipo de coleção Java.
Naturalmente, o código acima pode gerar erros de tempo de execução se usarmos o código herdado com os tipos de dados incorretos (por exemplo, se definirmos uma matriz como a "entrada" do StructuredViewer em vez de uma coleção Java).
Exemplo de chamada do método:
fonte
No mundo da Sessão HTTP, você realmente não pode evitar a transmissão, já que a API é escrita dessa maneira (apenas recebe e retorna
Object
).Com um pouco de trabalho, você pode facilmente evitar o elenco desmarcado. Isso significa que ele se transformará em um elenco tradicional, dando um
ClassCastException
direito no caso de um erro). Uma exceção desmarcada pode se transformar em umCCE
a qualquer momento mais tarde, em vez do ponto do elenco (essa é a razão pela qual é um aviso separado).Substitua o HashMap por uma classe dedicada:
Em seguida, faça a conversão para essa classe em vez de
Map<String,String>
e tudo será verificado no local exato em que você escreve seu código. Não é inesperadoClassCastExceptions
mais tarde.fonte
No Android Studio, se você deseja desativar a inspeção, pode usar:
fonte
Nesse caso em particular, eu não armazenaria o Maps diretamente no HttpSession, mas uma instância da minha própria classe, que por sua vez contém um Mapa (um detalhe de implementação da classe). Então você pode ter certeza de que os elementos no mapa são do tipo certo.
Mas se você quiser verificar se o conteúdo do mapa é do tipo correto, use um código como este:
fonte
A função de utilitário Objects.Unchecked na resposta acima de Esko Luontola é uma ótima maneira de evitar a confusão de programas.
Se você não deseja o SuppressWarnings em um método inteiro, o Java o força a colocá-lo em um local. Se você precisar de um elenco para um membro, pode levar a um código como este:
O uso do utilitário é muito mais limpo e ainda é óbvio o que você está fazendo:
NOTA: Acho importante acrescentar que, às vezes, o aviso realmente significa que você está fazendo algo errado, como:
O que o compilador está dizendo é que essa conversão NÃO será verificada no tempo de execução; portanto, nenhum erro de tempo de execução será gerado até você tentar acessar os dados no contêiner genérico.
fonte
A supressão de aviso não é uma solução. Você não deve fazer uma transmissão de dois níveis em uma declaração.
fonte
Um palpite rápido se você postar seu código pode dizer com certeza, mas você pode ter feito algo ao longo das linhas de
que produzirá o aviso quando você precisar fazer
pode valer a pena olhar
Genéricos na linguagem de programação Java
se você não estiver familiarizado com o que precisa ser feito.
fonte
Talvez eu tenha entendido mal a pergunta (um exemplo e algumas linhas ao redor seriam legais), mas por que você nem sempre usa uma interface apropriada (e Java5 +)? Não vejo nenhuma razão pela qual você queira converter para a em
HashMap
vez de aMap<KeyType,ValueType>
. Na verdade, não consigo imaginar nenhum motivo para definir o tipo de uma variável emHashMap
vez deMap
.E por que a fonte é uma
Object
? É um tipo de parâmetro de uma coleção herdada? Nesse caso, use genéricos e especifique o tipo que você deseja.fonte
Se eu tiver que usar uma API que não ofereça suporte a genéricos ... eu tento isolar essas chamadas em rotinas de wrapper com o mínimo de linhas possível. Em seguida, uso a anotação SuppressWarnings e também adiciono os lançamentos de segurança de tipo ao mesmo tempo.
Essa é apenas uma preferência pessoal para manter as coisas o mais organizadas possível.
fonte
Pegue este, é muito mais rápido do que criar um novo HashMap, se ele já for um, mas ainda seguro, pois cada elemento é verificado em relação ao seu tipo ...
fonte
key.isAssignableFrom(e.getKey().getClass())
pode ser escrito comokey.isInstance(e.getKey())
Basta verificar antes de lançá-lo.
E para quem pergunta, é bastante comum receber objetos em que você não tem certeza do tipo. Muitas implementações "SOA" herdadas passam por vários objetos nos quais você nem sempre deve confiar. (Os horrores!)
EDIT Alterou o código de exemplo uma vez para corresponder às atualizações do pôster e, após alguns comentários, vejo que instanceof não funciona muito bem com genéricos. No entanto, alterar a verificação para validar o objeto externo parece funcionar bem com o compilador de linha de comando. Exemplo revisado agora publicado.
fonte
Quase todo problema em Ciência da Computação pode ser resolvido adicionando um nível de indireção *, ou algo assim.
Portanto, introduza um objeto não genérico de nível superior que a
Map
. Sem contexto, não parecerá muito convincente, mas de qualquer maneira:* Exceto muitos níveis de indireção.
fonte
Aqui está uma maneira de lidar com isso quando substituo a
equals()
operação.Isso parece funcionar no Java 8 (mesmo compilado com
-Xlint:unchecked
)fonte
Se você tiver certeza de que o tipo retornado por session.getAttribute () é HashMap, não será possível converter para esse tipo exato, mas confie apenas na verificação do HashMap genérico
O Eclipse surpreenderá os avisos, mas é claro que isso pode levar a erros de tempo de execução que podem ser difíceis de depurar. Eu uso essa abordagem apenas em contextos não críticos à operação.
fonte
Duas maneiras, uma que evita a marca completamente, a outra usando um método utilitário impertinente, mas agradável.
O problema é coleções pré-genéricas ...
Eu acredito que a regra geral é: "converter objetos uma coisa de cada vez" - o que isso significa ao tentar usar classes brutas em um mundo genérico é que, porque você não sabe o que está neste mapa <?,?> (e, de fato, a JVM pode até achar que nem é um mapa!), é óbvio quando você pensa sobre isso que não pode convertê-lo. Se você tivesse um Map <String,?> Map2, o HashSet <String> keys = (HashSet <String>) map2.keySet () não emitirá um aviso, apesar de ser um "ato de fé" para o compilador (porque ele pode vir a ser um TreeSet) ... mas só é um único ato de fé.
PS com a objeção de que a iteração como na minha primeira maneira "é chata" e "leva tempo", a resposta é "sem dor, sem ganho": é garantido que uma coleção genérica contenha Map.Entry <String, String> se nada outro. Você tem que pagar por esta garantia. Ao usar genéricos sistematicamente, esse pagamento, maravilhosamente, assume a forma de conformidade com a codificação, não o tempo da máquina!
Uma escola de pensamento pode dizer que você deve definir as configurações do Eclipse para cometer erros de verificação não verificados, em vez de avisos. Nesse caso, você teria que usar o meu primeiro caminho.
fonte
Isso faz os avisos desaparecerem ...
fonte
Solução: Desative este aviso no Eclipse. Não @SuppressWarnings, apenas desative-o completamente.
Várias das "soluções" apresentadas acima estão fora de linha, tornando o código ilegível com o objetivo de suprimir um aviso bobo.
fonte
@SuppressWarnings
não torna o código ilegível.