Pergunta

Esta pergunta é solicitado por HashMap.put estranho ()

Eu acho que eu entendo por que Map<K,V>.put leva um K mas Map<K,V>.get leva uma Object, não parece fazê-lo vai quebrar o código muito existente.

Agora vamos entrar em um cenário muito propenso a erros:

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L,"Five"); // compiler barfs on m.put(5, "Five")
m.contains(5); // no complains from compiler, but returns false

Não podia ter sido resolvido, retornando true se o valor Long foi a uma gama int e os valores são iguais?

Foi útil?

Solução

Aqui está a fonte de Long.java

public boolean equals(Object obj) {
    if (obj instanceof Long) {
        return value == ((Long)obj).longValue();
    }
    return false;
}

i. ele precisa ser um tipo Long para ser igual. Acho que a diferença fundamental entre:

long l = 42L
int i = 42;
l == i

e seu exemplo acima é que com primitivos um alargamento implícita do valor int pode ocorrer, porém com tipos de objetos não existem regras para a conversão implícita de Integer para um longo.

Também confira Java Puzzlers , ele tem um monte de exemplos semelhantes a este.

Outras dicas

De modo geral, embora não seja estritamente expressa no contrato para equals () , objetos não deve considerar-se igual a outro objeto que não é da mesma classe exata (mesmo que seja uma subclasse). Considere a propriedade simétrica -. Se a.Equals (b) é verdadeiro, então b.equals (a) também deve ser verdade

Vamos ter dois objetos, foo de Super classe, e bar de Sub classe, que se estende Super. Agora, considere a implementação de equals() em Super, especificamente quando é chamado como foo.equals(bar). Foo só sabe que bar é fortemente tipado como um Object, de modo a obter uma comparação exata que precisa de verificar que é uma instância de Super e se não retornar falso. É, por isso, esta parte é muito bem. Agora compara todos os campos de instância, etc. (ou qualquer que seja a implementação comparação real é), e encontra-los iguais. Tão longe, tão bom.

No entanto, o contrato só pode retornar true se sabe que bar.equals (foo) vai retornar verdadeiro bem. Desde bar pode ser qualquer subclasse de Super, não está claro se o método equals () vai ser substituído (e se será provavelmente). Assim, para ter certeza de que sua implementação está correta, é preciso escrevê-lo de forma simétrica e garantir que os dois objetos são da mesma classe.

Mais fundamentalmente, objetos de diferentes classes não podem realmente ser considerados iguais -. Pois neste caso, apenas um deles pode ser inserido em um HashSet<Sub>, por exemplo

Sim, mas tudo se resume ao algoritmo de comparação e quão longe a tomar as conversões. Por exemplo, o que você quer que aconteça quando você tenta m.Contains("5")? Ou se você passar uma matriz com 5 como o primeiro elemento? Simplesmente falando, parece ser ligado até "se os tipos são diferentes, as chaves são diferentes".

Em seguida, tomar uma coleção com um object como a chave. O que você quer que aconteça se você put um 5L, em seguida, tentar obter 5, "5", ...? E se você put um 5L e uma 5 e uma "5" e você quer verificar se há uma 5F?

Uma vez que é uma coleção genérica (ou templated, ou o que você quiser chamá-lo), ele teria que verificar e fazer algumas especial comparando para certos tipos de valor. Se K é int em seguida, verificar se o objeto passado é long, short, float, double, ..., em seguida, converter e comparar. Se K é float em seguida, verificar se o objeto passado é ...

Você começa o ponto.

Outra aplicação poderia ter sido para lançar uma exceção se os tipos não corresponde, no entanto, e muitas vezes eu desejo que ele fez.

A sua questão parece razoável em sua face, mas seria uma violação das convenções gerais para equals (), se não seu contrato, para retornar verdadeiro para dois tipos diferentes.

Parte do projeto de linguagem Java foi para objetos para não converter implicitamente a outros tipos, ao contrário do C ++. Este foi parte de fazer Java uma pequena, uma linguagem simples. Uma parcela razoável de complexidade do C ++ vem de conversões implícitas e suas interações com outros recursos.

Além disso, Java tem uma dicotomia nítida e visível entre primitivos e objetos. Isto é diferente de outras línguas em que esta diferença está escondido sob as cobertas como uma otimização. Isso significa que você não pode esperar por muito tempo e Integer agir como longa e int.

código da biblioteca pode ser escrita para esconder essas diferenças, mas que pode realmente fazer mal, fazendo o ambiente de programação menos consistente.

Assim você código deve ser ....

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L, "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(5L)); // true

Você está esquecendo que java é autoboxing seu código, de modo que o código acima seria equivelenet para

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(new Long(5L), "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(new Long(5))); // true
System.out.println(m.containsKey(new Long(5L))); // true

Assim, uma parte do seu problema é a autoboxing. A outra parte é que você tem tipos diferentes como outras pôsteres declarou.

As outras respostas explicar adequadamente porque ele falha, mas nenhum deles abordar como escrever código que é menos propenso a erros torno desta questão. Ter que lembrar de adicionar tipo de-lança (sem compilador ajuda), primitivos sufixo com L e assim por diante não é apenas IMHO aceitável.

Eu recomendo usar a biblioteca tesouro GNU de coleções quando você tem primitivos (e em muitos outros casos). Por exemplo, há uma TLongLongHashMap que armazena coisas interally como longs primitivos. Como resultado, você nunca acabar com o boxe / unboxing, e nunca acabar com comportamentos inesperados:

TLongLongHashMap map = new TLongLongHashMap();
map.put(1L, 45L);
map.containsKey(1); // returns true, 1 gets promoted to long from int by compiler
int x = map.get(1); // Helpful compiler error. x is not a long
int x = (int)map.get(1); // OK. cast reassures compiler that you know
long x = map.get(1); // Better.

e assim por diante. Não há necessidade de obter o tipo certo, e o compilador dá-lhe um erro (que você pode corrigir ou substituir) se você fizer algo bobo (tentar armazenar um longo em um int).

As regras da média que as comparações funcionar correctamente, bem-casting auto:

if(map.get(1) == 45) // 1 promoted to long, 45 promoted to long...all is well

Como um bônus, a sobrecarga de memória e desempenho de tempo de execução é muito melhor.

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