Por que .compareTo () está em uma interface enquanto .equals () está em uma classe em Java?

30

Quero saber por que o .compareTo()está na Comparableinterface enquanto um método como .equalsestá na Objectclasse. Para mim, parece arbitrário o motivo de um método como esse .compareTo()ainda não estar na Objectclasse.

Para usar .compareTo(), você implementa a Comparableinterface e implementa o .compareTo()método para seus propósitos. Para o .equals()método, você simplesmente substitui o método em sua classe, pois todas as classes são herdadas da Objectclasse.

Minha pergunta é por que um método é semelhante a .compareTo()uma interface que você implementa, e não a uma classe Object? Da mesma forma, por que o .equals()método da classe Objecte não em alguma interface deve ser implementado?

Wesley
fonte
2
É uma escolha de design da linguagem Java (não significa necessariamente que foi a escolha certa). Em outros idiomas, por exemplo, Haskell , você precisa implementar a interface de igualdade para obter igualdade de valor (na verdade, você fornece uma instância para a Eqclasse).
Mucaho
2
Meta-pergunta relacionada: essas perguntas são duplicadas?

Respostas:

58

Nem todos os objetos podem ser comparados, mas todos os objetos podem ser verificados quanto à igualdade. Se nada mais, é possível ver se existem dois objetos no mesmo local na memória (igualdade de referência).

O que isso significa compareTo()em dois Threadobjetos? Como um segmento é "maior que" outro? Como você compara dois ArrayList<T>s?

O Objectcontrato se aplica a todas as classes Java. Se nem uma classe puder ser comparada a outras instâncias de sua própria classe, Objectnão será possível exigir que ela faça parte da interface.

Joshua Bloch usa as palavras-chave "ordenação natural" ao explicar por que uma classe pode querer implementar Comparable. Nem toda classe tem uma ordem natural, como mencionei nos meus exemplos acima, portanto nem toda classe deve implementar Comparablenem deve Objectter o compareTométodo.

... o compareTométodo não está declarado em Object. ... É de caráter semelhante ao método de Objects equals, exceto pelo fato de permitir comparações de ordem, além de comparações simples de igualdade, e é genérico. Ao implementar Comparable, uma classe indica que suas instâncias têm uma ordem natural .

Java eficaz, segunda edição : Joshua Bloch. Item 12, Página 62. As elipses removem referências a outros capítulos e exemplos de código.

Para os casos onde não querem impor uma ordenação em um não- Comparableclasse que não tem uma ordem natural, você sempre pode fornecer uma Comparatorinstância para ajudar a resolver isso.


fonte
3
Há ainda mais divertido quando você começa a pensar sobre compareTo com exceção (que não é uma classe abstrata e, assim, teria aplicado-lhe significado aNullPointerException.compareTo (anUnsupportedFlavorException) teria ... significado?
10
todos os objetos podem ser verificados quanto à igualdade. Em Java, sim, mas em geral não. Existem alguns exemplos de objetos em que a comparação de igualdade não faz sentido (nem mesmo faz referência a igualdade) - por exemplo, singletons. Pode haver interfaces (classes abstratas) como ValueEquality e ReferenceEquality. Ele pode até não ser tão má ideia ...
QbD
5
"todos os objetos podem ser verificados quanto à igualdade. Se nada mais, é possível ver se existem dois objetos no mesmo local na memória (igualdade de referência)." - já que temos ==para o último, isso tem um anel oco. Desconsiderando o padrão redundante, pode-se encontrar razões válidas para não assumir equalsem todas as classes, pois nem todos os tipos podem suportar uma relação de equivalência.
Raphael
3
Dois exemplos de tipos em que não é sensato definir igualdade: fluxos (por exemplo, listas potencialmente infinitas preguiçosas ou números de precisão infinitos) e funções. O primeiro tem o problema de que estabelecer igualdade pode exigir uma comparação infinita. Decidir se duas funções são iguais é indecidível. Perguntar se duas instâncias desses tipos existem no mesmo local de memória é 1) não muito útil e 2) permite que os clientes escrevam um código sensível ao que deve ser o detalhe da implementação. Hoje eu posso lhe dar uma nova instância da mesma lista infinita cada vez que você pedir, amanhã eu posso memorizá-la.
Doval
6
@ Snowman O fato de ser inútil, combinado com o fato de expor os detalhes da implementação, é motivo suficiente para não permitir. Praticamente todas as classes "baseadas em valores" no Java 8 têm alguns clichês dizendo "Não somos responsáveis ​​pelo que acontece se você usar ==", porque o modo como essas classes são instanciadas é um detalhe da implementação, mas a linguagem torna impossível ocultar. Você poderia dizer que qualquer um que compare dois Integers por referência é um idiota, mas permitir que a comparação comece ainda é mais tedioso.
Doval
8

O JLS §4.3.2 define o classobjeto da seguinte maneira:

4.3.2 O objeto de classe

A classe Objecté uma superclasse (§8.1.4) de todas as outras classes.

Todos os tipos de classe e matriz herdam (§8.4.8) os métodos de classe Object, resumidos da seguinte forma:

  • O método cloneé usado para criar uma duplicata de um objeto.

  • O método equalsdefine uma noção de igualdade de objeto, que se baseia em comparação de valor, não de referência.

  • O método finalizeé executado imediatamente antes de um objeto ser destruído (§12.6).

  • O método getClassretorna o objeto Class que representa a classe do objeto.

  • Existe um Classobjeto para cada tipo de referência. Ele pode ser usado, por exemplo, para descobrir o nome completo de uma classe, seus membros, sua superclasse imediata e quaisquer interfaces que ela implemente.

    O tipo de expressão de invocação de método getClassé Class<? extends |T|>onde Té pesquisada a classe ou interface (§15.12.1) getClass.

    Um método de classe declarado synchronized(§8.4.3.6) é sincronizado no monitor associado ao objeto Class da classe.

  • O método hashCodeé muito útil, juntamente com o método igual, em hashtables como java.util.Hashmap.

  • Os métodos wait, notifye notifyAllsão utilizados em linhas de programação utilizando simultâneas (§17.2).

  • O método toStringretorna uma representação String do objeto.

Então, é por isso que equalsestá dentro, Objectmas compareToestá em uma interface separada. Eu especularia que eles queriam manter Objecto mínimo possível. Eles provavelmente imaginaram que quase todos Objects precisariam equalse hashCode(que é realmente apenas uma forma de teste de igualdade), mas nem todos os objetos precisariam ter um conceito de ordenação , para o que compareToé usado.

durron597
fonte
Eu acho que teoricamente poderia haver uma interface, Equitable<T>mas se Objectimplementada, então toda classe seria uma Equitable<Object>. Nesse ponto, há alguma diferença? Na verdade, na verdade não, em complexidade, sim.
Captain Man
11
@CaptainMan No .Net, objecttem Equals(object), assim como em Java, mas também há a IEquatable<T>interface. Embora o principal motivo para sua existência seja evitar o boxe quando Tfor um tipo de valor, o que não é possível em Java.
Svick
hashCode não é uma forma de teste de igualdade, porque existem colisões de hash. se A e B são iguais, eles têm o mesmo hashCode, mas se A e B têm o mesmo hashCode, isso não significa que eles são iguais!
Josef
na verdade, sua resposta se beneficiaria muito com a mudança para um JLS mais antigo ( titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf ) - ele possui uma citação muito melhor do motivo pelo qual equalsé declarado Objectdiretamente: The methods equals and hashCode are declared for the benefit of hashtables such as java.util.Hashtable (§21.7)- como projeto em Java é compatível com versões anteriores, a escolha do design do Java 1.0 é a verdadeira razão de equalsestar onde está.
vaxquis
como a edição que propus provavelmente será rejeitada por ser "muito drástica", caso você queira apenas pressionar Ctrl + C / Ctrl + V o material relevante em sua resposta: pastebin.com/8c4EpLRX
vaxquis
2

Além da excelente resposta do Snowman, lembre-se de que essa Comparableinterface é genérica há muito tempo. Um tipo não implementa compareTo(object), implementa compareTo(T)onde Té seu próprio tipo. Isso não pode ser implementado object, pois objectnão conhece a classe que será derivada dela.

objectpoderia ter definido um compareTo(object)método, mas isso teria permitido não apenas o que Snowman aponta, uma comparação entre dois ArrayList<T>s ou entre dois Threads, mas também uma comparação entre um ArrayList<T>e a Thread. Isso é ainda mais absurdo.

hvd
fonte
0

Suponha que eu tenha duas referências a objetos: X identifica uma instância de Stringretenção do conteúdo "George"; Y identifica a instância de Pointretenção das coordenadas [12,34]. Considere as duas perguntas a seguir:

  • X e Y identificam objetos equivalentes?

  • X deve classificar antes, depois ou equivalente a Y?

O fato de X e Y identificarem instâncias de tipos não relacionados não apresenta nenhum problema ao considerar a primeira pergunta. Objetos só podem ser considerados equivalentes se seus tipos compartilharem uma base comum que os define como equivalentes; uma vez que Stringe Pointnão possui essa base (seu único tipo de base comum considera todos os objetos distintos como não equivalentes), a resposta é simplesmente "não".

O fato de os tipos não serem relacionados, no entanto, coloca um enorme problema em relação à segunda questão. Alguns tipos de definir relações de ordenação entre os seus casos, e algumas relações de ordenação pode mesmo estender-se por vários tipos [por exemplo, seria possível para BigIntegere BigDecimaldefinir métodos de comparação que permitiriam instâncias de qualquer tipo para ser classificado em relação a instâncias do outro], mas em geral, não é possível tomar duas instâncias arbitrárias e perguntar "Deveria X ordenar antes, depois ou equivalente a Y" e derivar uma ordem total. Seria possível perguntar "Deve X ordenar antes, depois, equivalente ou sem classificação em relação a Y" se os objetos precisarem relatar uma ordem consistente, mas não um totalum, mas a maioria dos algoritmos de classificação exige pedidos totais. Assim, mesmo que todos os objetos pudessem implementar um compareTométodo se "sem classificação" fosse um retorno válido, esse método não seria útil o suficiente para justificar sua existência.

supercat
fonte