Aqui está uma bela armadilha que acabei de encontrar. Considere uma lista de números inteiros:
List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(6);
list.add(7);
list.add(1);
Algum palpite sobre o que acontece quando você executa list.remove(1)
? Que tal list.remove(new Integer(1))
? Isso pode causar alguns erros desagradáveis.
Qual é a maneira correta de diferenciar entre remove(int index)
, que remove um elemento de um determinado índice e remove(Object o)
, que remove um elemento por referência, ao lidar com listas de números inteiros?
O ponto principal a ser considerado aqui é o que a @Nikita mencionou - a correspondência exata de parâmetros tem precedência sobre o boxe automático.
java
collections
overloading
Yuval Adam
fonte
fonte
Respostas:
Java sempre chama o método que melhor se adequa ao seu argumento. O boxe automático e o upcasting implícito são realizados apenas se não houver um método que possa ser chamado sem transmissão / boxing automático.
A interface da lista especifica dois métodos de remoção (observe os nomes dos argumentos):
remove(Object o)
remove(int index)
Isso significa que
list.remove(1)
remove o objeto na posição 1 eremove(new Integer(1))
remove a primeira ocorrência do elemento especificado dessa lista.fonte
Integer.valueOf(1)
é melhor prática do quenew Integer(1)
. O método estático pode fazer cache e outros, para que você obtenha melhor desempenho.new Integer(1)
, mas eu concordo queInteger.valueOf(1)
ou(Integer) 1
sejam equivalentes.Você pode usar casting
e
Não importa se n é int ou Inteiro, o método sempre chamará o que você espera.
Usar
(Integer) n
ouInteger.valueOf(n)
é mais eficiente donew Integer(n)
que os dois primeiros pode usar o cache Inteiro, enquanto o último sempre criará um objeto.fonte
Não sei o caminho 'adequado', mas o modo como você sugeriu funciona muito bem:
remove o elemento em determinada posição e
remove o objeto especificado da lista.
Isso ocorre porque a VM tenta primeiro encontrar o método declarado com exatamente o mesmo tipo de parâmetro e só então tenta autoboxing.
fonte
list.remove(4)
é uma correspondência exata delist.remove(int index)
, por isso será chamado. Se você quiser chamadalist.remove(Object)
faça o seguinte:list.remove((Integer)4)
.fonte
(Integer)
elenco simples como você escreveu acima parece ser a abordagem mais fácil para mim.Não há necessidade de adivinhar. O primeiro caso resultará na
List.remove(int)
chamada e o elemento na posição1
será removido. O segundo caso resultará naList.remove(Integer)
chamada e o elemento cujo valor é igual aInteger(1)
será removido. Nos dois casos, o compilador Java seleciona a sobrecarga correspondente mais próxima.Sim, existe potencial para confusão (e bugs) aqui, mas é um caso de uso bastante incomum.
Quando os dois
List.remove
métodos foram definidos no Java 1.2, as sobrecargas não eram ambíguas. O problema surgiu apenas com a introdução de genéricos e autoboxing no Java 1.5. Em retrospectiva, teria sido melhor se um dos métodos de remoção tivesse um nome diferente. Mas agora é tarde demais.fonte
Observe que, mesmo que a VM não faça a coisa certa, o que faz, você ainda poderá garantir o comportamento adequado usando o fato de
remove(java.lang.Object)
operar em objetos arbitrários:fonte
equals
método, especificamente (do Javadoc) "É simétrico: para qualquer valor de referência não nulo xey, x.equals (y) deve retornar true se e somente se y.equals ( x) retorna verdadeiro. ". Como tal, não é garantido que funcione em todas as implementações deList
, porque qualquer implementação da List tem permissão para trocar x e yx.equals(y)
à vontade, já que o JavadocObject.equals
diz que isso deve ser válido.Simplesmente gostei de seguir, conforme sugerido por #decitrig, na resposta aceita primeiro comentário.
Isso me ajudou. Agradecemos novamente #decitrig pelo seu comentário. Pode ajudar para alguém.
fonte
Bem, aqui está o truque.
Vamos dar dois exemplos aqui:
Agora vamos dar uma olhada na saída:
Agora vamos analisar a saída:
Quando 3 é removido da coleção, ele chama o
remove()
método da coleção que tomaObject o
como parâmetro. Portanto, ele remove o objeto3
. Mas no objeto arrayList, ele é substituído pelo índice 3 e, portanto, o quarto elemento é removido.Pela mesma lógica da remoção de objetos, null é removido nos dois casos na segunda saída.
Portanto, para remover o número
3
que é um objeto, precisaremos explicitamente passar 3 como umobject
.E isso pode ser feito ao transmitir ou agrupar usando a classe wrapper
Integer
.Por exemplo:
fonte