Perché .compareTo () in un'interfaccia mentre .equals () è in una classe in Java?


30

Voglio sapere perché .compareTo()è Comparablenell'interfaccia mentre un metodo simile .equalsè nella Objectclasse. A me sembra arbitrario il motivo per cui un metodo simile .compareTo()non è Objectgià nella classe.

Per usare .compareTo(), implementate l' Comparableinterfaccia e implementate il .compareTo()metodo per i vostri scopi. Per il .equals()metodo, devi semplicemente sostituire il metodo nella tua classe, poiché tutte le classi ereditano dalla Objectclasse.

La mia domanda è: perché un metodo è come .compareTo()in un'interfaccia che implementi piuttosto che in una classe come Object? Allo stesso modo, perché deve essere implementato il .equals()metodo nella classe Objecte non in qualche interfaccia?



2
È una scelta progettuale del linguaggio Java (non significa necessariamente che sia stata la scelta giusta). In altre lingue, ad esempio Haskell , è necessario implementare l'interfaccia di uguaglianza per ottenere l'uguaglianza di valore (in realtà si fornisce un'istanza alla Eqtabella dei tipi).
mucaho,

Risposte:


58

Non tutti gli oggetti possono essere confrontati, ma tutti gli oggetti possono essere controllati per l'uguaglianza. Se non altro, si può vedere se esistono due oggetti nella stessa posizione nella memoria (uguaglianza di riferimento).

Cosa significa compareTo()su due Threadoggetti? In che modo un thread è "maggiore di" un altro? Come si confrontano due ArrayList<T>s?

Il Objectcontratto si applica a tutte le classi Java. Se anche una sola classe non può essere confrontata con altre istanze della sua stessa classe, allora Objectnon può richiedere che faccia parte dell'interfaccia.

Joshua Bloch usa le parole chiave "ordinamento naturale" quando spiega perché una classe potrebbe voler implementare Comparable. Non tutte le classi hanno un ordinamento naturale come ho menzionato nei miei esempi precedenti, quindi non tutte le classi dovrebbero implementare Comparablené dovrebbero Objectavere il compareTometodo.

... il compareTometodo non è dichiarato in Object. ... E 'la stessa natura di Object's equalsmetodo, tranne che consente confronti di ordine, oltre a semplici confronti di uguaglianza, ed è generica. Implementando Comparable, una classe indica che le sue istanze hanno un ordinamento naturale .

Efficace Java, seconda edizione : Joshua Bloch. Articolo 12, Pagina 62. Le ellissi rimuovono i riferimenti ad altri capitoli ed esempi di codice.

Per i casi in cui si fa vuole imporre un ordinamento su un non- Comparableclasse che non ha un ordinamento naturale, si può sempre fornire un Comparatoresempio per aiutare a risolvere esso.


3
C'è ancora più divertimento quando inizi a pensare a compareTo to Exception (che non è una classe astratta e quindi l'avrebbe implementata nel senso che aNullPointerException.compareTo (anUnsupportedFlavorException) avrebbe ... significato?

10
tutti gli oggetti possono essere controllati per l'uguaglianza In Java sì, ma in generale no. Ci sono alcuni esempi di oggetti in cui il confronto di uguaglianza non ha senso (nemmeno riferimento all'uguaglianza) - ad esempio i singoli. Potrebbero esserci interfacce (classi astratte) come ValueEquality e ReferenceEquality. Potrebbe anche non essere una cattiva idea ...
qbd

5
"Tutti gli oggetti possono essere controllati per l'uguaglianza. Se non altro, si può vedere se esistono due oggetti nella stessa posizione in memoria (uguaglianza di riferimento)." - dal momento che abbiamo ==per quest'ultimo, questo ha un anello vuoto. Ignorando il valore predefinito ridondante, si possono trovare validi motivi per non assumere equalssu tutte le classi, poiché non tutti i tipi possono supportare una relazione di equivalenza.
Raffaello

3
Due esempi di tipi in cui non è sensato definire l'uguaglianza: flussi (ad es. Elenchi pigri potenzialmente infiniti o numeri di precisione infiniti) e funzioni. Il primo ha il problema che stabilire l'uguaglianza può richiedere un confronto infinito. Decidere se due funzioni sono uguali è indecidibile. Chiedere se esistono due istanze di questi tipi nella stessa posizione di memoria è 1) non molto utile e 2) consente ai client di scrivere codice sensibile ai dettagli dell'implementazione. Oggi posso darti una nuova istanza dello stesso infinito elenco ogni volta che lo chiedi, domani posso memorizzarlo.
Doval,

6
@Snowman Il fatto che sia inutile combinato con il fatto che espone i dettagli di implementazione è una ragione sufficiente per non permetterlo. Praticamente ogni classe "basata sul valore" in Java 8 ha alcuni esempi che dicono "Non siamo responsabili di ciò che accade se si utilizza ==" perché il modo in cui tali classi sono istanziate è un dettaglio di implementazione ma il linguaggio rende impossibile nasconderlo. Si potrebbe dire che chiunque si confrontano due Integers per riferimento è un idiota, ma che permette il confronto per cominciare è più stupidi ancora.
Doval,

8

I JLS §4.3.2 definisce l' classoggetto nel seguente modo:

4.3.2. L'oggetto classe

La classe Objectè una superclasse (§8.1.4) di tutte le altre classi.

Tutti i tipi di classi e array ereditano (§8.4.8) i metodi della classe Object, che sono riassunti come segue:

  • Il metodo cloneviene utilizzato per creare un duplicato di un oggetto.

  • Il metodo equalsdefinisce una nozione di uguaglianza di oggetti, che si basa sul confronto tra valore, non riferimento.

  • Il metodo finalizeviene eseguito appena prima che un oggetto venga distrutto (§12.6).

  • Il metodo getClassrestituisce l'oggetto Class che rappresenta la classe dell'oggetto.

  • ClassEsiste un oggetto per ciascun tipo di riferimento. Può essere utilizzato, ad esempio, per scoprire il nome completo di una classe, i suoi membri, la sua superclasse immediata e tutte le interfacce che implementa.

    Il tipo di espressione del metodo di invocazione getClassè Class<? extends |T|>dove si Ttrova la classe o l'interfaccia cercata (§15.12.1) getClass.

    Un metodo di classe dichiarato synchronized(§8.4.3.6) si sincronizza sul monitor associato all'oggetto Class della classe.

  • Il metodo hashCodeè molto utile, insieme al metodo uguale, in hashtabili come java.util.Hashmap.

  • I metodi wait, notifye notifyAllsono utilizzati nella programmazione concorrente mediante thread (§17.2).

  • Il metodo toStringrestituisce una rappresentazione String dell'oggetto.

Quindi, ecco perché equalsè dentro Objectma compareToè in un'interfaccia separata. Vorrei ipotizzare che volessero mantenere Objectil minimo possibile. Probabilmente hanno pensato che quasi tutti Objects avrebbero avuto bisogno ( equalse hashCodeche in realtà è solo una forma di test di uguaglianza), ma non tutti gli oggetti avrebbero bisogno di avere un concetto di ordinamento , che è quello per cui compareToviene utilizzato.


Immagino che teoricamente potrebbe esserci un'interfaccia, Equitable<T>ma se Objectimplementata, ogni classe sarebbe una Equitable<Object>. A quel punto, c'è una differenza? In effetti non proprio, in complessità sì.
Captain Man

1
@CaptainMan In .Net, objectha Equals(object), proprio come in Java, ma c'è anche l' IEquatable<T>interfaccia. Anche se il motivo principale per cui esiste è evitare il pugilato quando Tè un tipo di valore, cosa impossibile in Java.
svick,

hashCode non è una forma di test di uguaglianza, perché ci sono collisioni di hash. se A e B sono uguali, hanno lo stesso hashCode, ma se A e B hanno lo stesso hashCode, ciò non significa che siano uguali!
Josef,

in realtà, la tua risposta trarrebbe grande beneficio dal passaggio a un JLS più vecchio ( titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf ) - ha una citazione molto migliore sul perché equalsè dichiarato Objectdirettamente: The methods equals and hashCode are declared for the benefit of hashtables such as java.util.Hashtable (§21.7)- come design Java è compatibile con le versioni precedenti, la scelta del design Java 1.0 è la vera ragione per equalsessere dove si trova.
vaxquis,

poiché la modifica che ho proposto verrà probabilmente respinta per essere "troppo drastica", nel caso in cui si desideri solo Ctrl + C / Ctrl + V gli elementi pertinenti nella risposta: pastebin.com/8c4EpLRX
vaxquis

2

Oltre all'ottima risposta di Snowman, ricorda che Comparableè stata un'interfaccia generica per molto tempo. Un tipo non implementa compareTo(object), implementa compareTo(T)dov'è il Tsuo tipo. Questo non può essere implementato object, poiché objectnon conosce la classe che ne deriverà.

objectavrebbe potuto definire un compareTo(object)metodo, ma ciò avrebbe consentito non solo ciò che Snowman indica, un confronto tra due ArrayList<T>s o tra due Threads, ma anche un confronto tra an ArrayList<T>e a Thread. È ancora più assurdo.


0

Supponiamo che io abbia due riferimenti a oggetti: X identifica un'istanza di Stringcontenere il contenuto "George"; Y identifica l'istanza di Pointmantenimento delle coordinate [12,34]. Considera le seguenti due domande:

  • X e Y identificano oggetti equivalenti?

  • X dovrebbe ordinare prima, dopo o equivalente a Y?

Il fatto che X e Y identifichino istanze di tipi non correlati non pone alcun problema quando si considera la prima domanda. Gli oggetti possono essere considerati equivalenti solo se i loro tipi condividono una base comune che li definisce come equivalenti; poiché Stringe Pointnon hanno una tale base (il loro unico tipo di base comune considera tutti gli oggetti distinti come non equivalenti) la risposta è semplicemente "no".

Il fatto che i tipi non siano correlati, tuttavia, pone un enorme problema per quanto riguarda la seconda domanda. Alcuni tipi definiscono le relazioni di ordinamento tra le loro istanze e alcune relazioni di ordinamento possono persino estendersi su più tipi [ad esempio, sarebbe possibile BigIntegere BigDecimaldefinire metodi di confronto che consentirebbero alle istanze di entrambi i tipi di essere classificate rispetto alle istanze dell'altra], ma in generale non è possibile prendere due istanze arbitrarie e chiedere "X dovrebbe ordinare prima, dopo o equivalente a Y" e ricavare un ordine totale. Sarebbe possibile chiedere "Se X dovesse ordinare prima, dopo, equivalente o non classificato rispetto a Y" se gli oggetti fossero tenuti a segnalare un ordinamento coerente ma non un totaleuno, ma la maggior parte degli algoritmi di ordinamento richiedono ordinamenti totali. Pertanto, anche se tutti gli oggetti fossero in grado di implementare un compareTometodo se "non classificato" fosse un ritorno valido, tale metodo non sarebbe abbastanza utile da giustificarne l'esistenza.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.