¿Por qué está .compareTo () en una interfaz mientras que .equals () está en una clase en Java?

30

Quiero saber por qué .compareTo()está en la Comparableinterfaz mientras que un método como .equalsestá en la Objectclase. Para mí, parece arbitrario por qué un método como .compareTo()este no está en la Objectclase ya.

Para usar .compareTo(), implementa la Comparableinterfaz e implementa el .compareTo()método para sus propósitos. Para el .equals()método, simplemente anule el método en su clase, ya que todas las clases heredan de la Objectclase.

Mi pregunta es ¿por qué es un método como .compareTo()en una interfaz que implementa en lugar de en una clase como Object? Del mismo modo, ¿por qué es el .equals()método en la clase Objecty no en alguna de las interfaces a implementar?

Wesley
fuente
2
Es una elección de diseño del lenguaje Java (no necesariamente significa que fue la elección correcta). En otros idiomas, por ejemplo, Haskell , tiene que implementar la interfaz de la igualdad para conseguir la igualdad de valor (en realidad le proporcionará una instancia de la Eqclase de tipos).
mucaho
2
Meta pregunta relacionada: ¿Son estas preguntas duplicadas entre sí?

Respuestas:

58

No se pueden comparar todos los objetos, pero se puede verificar la igualdad de todos los objetos. Por lo menos, uno puede ver si existen dos objetos en la misma ubicación en la memoria (igualdad de referencia).

¿Qué significa para compareTo()dos Threadobjetos? ¿Cómo es un hilo "mayor que" otro? ¿Cómo se comparan dos ArrayList<T>s?

El Objectcontrato se aplica a todas las clases de Java. Si incluso una clase no puede compararse con otras instancias de su propia clase, entonces Objectno puede requerir que sea parte de la interfaz.

Joshua Bloch usa las palabras clave "ordenamiento natural" cuando explica por qué una clase podría querer implementar Comparable. No todas las clases tienen un orden natural como mencioné en mis ejemplos anteriores, por lo que no todas las clases deberían implementar Comparableni deberían Objecttener el compareTométodo.

... el compareTométodo no está declarado en Object. ... Es similar en carácter a Object's equalsmétodo, excepto que permite comparaciones de orden, además de las comparaciones de igualdad simples, y es genérico. Al implementar Comparable, una clase indica que sus instancias tienen un orden natural .

Efectivo Java, segunda edición : Joshua Bloch. Elemento 12, página 62. Las elipses eliminan referencias a otros capítulos y ejemplos de código.

Para los casos en que no desea imponer un orden en un no- Comparableclase que no tiene un orden natural, siempre se puede suministrar una Comparatorinstancia para ayudar a resolver la misma.


fuente
3
No es aún más divertido cuando se comienza a pensar acerca compareTo con excepción (que no es una clase abstracta y por lo tanto habría puesto en práctica lo que significa aNullPointerException.compareTo (anUnsupportedFlavorException) tendría sentido ...?
10
todos los objetos pueden ser verificados para igualdad en Java sí, pero en general no. Hay algunos ejemplos de objetos donde la comparación de igualdad no tiene sentido (ni siquiera la igualdad de referencia), por ejemplo, singletons. Podría haber interfaces (clases abstractas) como ValueEquality y ReferenceEquality. Se podría incluso no ser tan mala idea ...
QBD
55
"se puede verificar la igualdad de todos los objetos. Si nada más, se puede ver si existen dos objetos en la misma ubicación en la memoria (igualdad de referencia)". - Como tenemos ==para este último, tiene un anillo hueco. Sin tener en cuenta el valor predeterminado redundante, se pueden encontrar razones válidas para no asumir equalsen todas las clases, ya que no todos los tipos pueden admitir una relación de equivalencia.
Rafael
3
Dos ejemplos de tipos en los que no es razonable definir la igualdad: flujos (por ejemplo, listas potencialmente infinitas perezosas o números de precisión infinitos) y funciones. El primero tiene el problema de que establecer la igualdad puede requerir una comparación infinita. Decidir si dos funciones son iguales es indecidible. Preguntar si existen dos instancias de estos tipos en la misma ubicación de memoria es 1) poco útil y 2) permite a los clientes escribir código sensible a lo que deberían ser detalles de implementación. Hoy puedo darle una nueva instancia de la misma lista infinita cada vez que pregunte, mañana puedo recordarlo.
Doval
66
@Snowman El hecho de que sea inútil combinado con el hecho de que expone los detalles de implementación es razón suficiente para no permitirlo. Casi todas las clases "basadas en el valor" en Java 8 tienen algunos comentarios que dicen "No somos responsables de lo que sucede si lo usas ==" porque la forma en que se instancian esas clases es un detalle de implementación, pero el lenguaje hace que sea imposible ocultarlo. Se podría decir que cualquiera que compare dos Integers por referencia es un idiota, pero permitir que la comparación comience es aún más tonta.
Doval
8

El JLS §4.3.2 define el classobjeto de la siguiente manera:

4.3.2. El objeto de clase

La clase Objectes una superclase (§8.1.4) de todas las demás clases.

Todos los tipos de clase y matriz heredan (§8.4.8) los métodos de clase Object, que se resumen a continuación:

  • El método clonese usa para hacer un duplicado de un objeto.

  • El método equalsdefine una noción de igualdad de objetos, que se basa en el valor, no en la referencia, en la comparación.

  • El método finalizese ejecuta justo antes de que se destruya un objeto (§12.6).

  • El método getClassdevuelve el objeto Class que representa la clase del objeto.

  • Existe un Classobjeto para cada tipo de referencia. Se puede utilizar, por ejemplo, para descubrir el nombre completo de una clase, sus miembros, su superclase inmediata y cualquier interfaz que implemente.

    El tipo de expresión de invocación de un método getClasses Class<? extends |T|>dónde Tse busca la clase o interfaz (§15.12.1) getClass.

    Un método de clase que se declara synchronized(§8.4.3.6) se sincroniza en el monitor asociado con el objeto Clase de la clase.

  • El método hashCodees muy útil, junto con el método igual, en tablas hash como java.util.Hashmap.

  • Los métodos wait, notifyy notifyAllse usan en la programación concurrente usando hilos (§17.2).

  • El método toStringdevuelve una representación de cadena del objeto.

Entonces, por eso equalsestá dentro Objectpero compareToestá en una interfaz separada. Supondría que querían mantener lo Objectmás mínimo posible. Probablemente pensaron que casi todos Objects necesitarían equalsy hashCode(que en realidad es solo una forma de prueba de igualdad), pero no todos los objetos necesitarían tener un concepto de ordenamiento , que es para lo que compareTose utiliza.

durron597
fuente
Supongo que teóricamente podría haber una interfaz, Equitable<T>pero si se Objectimplementa, entonces cada clase sería un Equitable<Object>. En ese punto, ¿hay alguna diferencia? En efecto, no realmente, en complejidad sí.
Capitán Man
1
@CaptainMan En .Net, objecttiene Equals(object), al igual que en Java, pero también está la IEquatable<T>interfaz. Aunque la razón principal para que exista es evitar el boxeo cuando Tes un tipo de valor, lo cual no es posible en Java.
svick
hashCode no es una forma de prueba de igualdad, porque hay colisiones hash. si A y B son iguales, tienen el mismo código hash, pero si A y B tienen el mismo código hash, ¡esto no significa que sean iguales!
Josef
en realidad, su respuesta se beneficiaría enormemente de cambiar a un JLS anterior ( titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf ): tiene una cita mucho mejor sobre por qué equalsse declara Objectdirectamente: The methods equals and hashCode are declared for the benefit of hashtables such as java.util.Hashtable (§21.7)- como diseño de Java es compatible con versiones anteriores, la elección del diseño de Java 1.0 es la razón real de equalsestar donde está.
vaxquis
dado que la edición que he propuesto probablemente será rechazada por ser "demasiado drástica", en caso de que desee simplemente Ctrl + C / Ctrl + V lo relevante en su respuesta: pastebin.com/8c4EpLRX
vaxquis
2

Además de la excelente respuesta de Snowman, recuerde que Comparableha sido una interfaz genérica durante mucho tiempo. Un tipo no se implementa compareTo(object), implementa compareTo(T)donde Tes su propio tipo. Esto no se puede implementar object, ya objectque no conoce la clase que se derivará de él.

objectpodría haber definido un compareTo(object)método, pero esto habría permitido no solo lo que señala Snowman, una comparación entre dos ArrayList<T>so dos Threads, sino incluso una comparación entre an ArrayList<T>y a Thread. Eso es aún más absurdo.

hvd
fuente
0

Supongamos que tengo dos referencias de objeto: X identifica una instancia de Stringcontenido "George"; Y identifica la instancia de Pointmantener las coordenadas [12,34]. Considere las siguientes dos preguntas:

  • ¿X e Y identifican objetos equivalentes?

  • ¿Debería X ordenar antes, después o equivalente a Y?

El hecho de que X e Y identifiquen instancias de tipos no relacionados no plantea ningún problema al considerar la primera pregunta. Los objetos solo pueden considerarse equivalentes si sus tipos comparten una base común que los define como equivalentes; desde Stringy Pointno tienen tales base (su único tipo de base común se refiere a todos los objetos distintos como no-equivalente) la respuesta es simplemente "no".

Sin embargo, el hecho de que los tipos no estén relacionados plantea un gran problema con respecto a la segunda pregunta. Algunos tipos definen relaciones de orden entre sus instancias, y algunas relaciones de orden pueden incluso extenderse sobre múltiples tipos [por ejemplo, sería posible BigIntegery BigDecimaldefinir métodos de comparación que permitirían clasificar las instancias de cualquier tipo en relación con las instancias del otro], pero En general, no es posible tomar dos instancias arbitrarias y preguntar "Debería X ordenar antes, después o equivalente a Y" y derivar un orden total. Sería posible preguntar "¿Debería X ordenar antes, después, equivalente o sin clasificar con respecto a Y" si los objetos tuvieran que informar un orden consistente aunque no un total?uno, pero la mayoría de los algoritmos de ordenación requieren pedidos totales. Por lo tanto, incluso si todos los objetos pudieran implementar un compareTométodo si "sin clasificar" fuera un retorno válido, dicho método no sería lo suficientemente útil como para justificar su existencia.

Super gato
fuente