Domanda

Ho un gruppo di classe che implementa un'interfaccia comune: Command.

E questo gruppo di classi va su una mappa.

Per far funzionare correttamente la Mappa, ho bisogno per ogni classe che implementa Command di ignorare il metodo Object.equals (Object other) .

va bene.

Ma vorrei forzare l'override degli uguali. = & Gt; Avere un errore di compilazione quando qualcosa che implementa il comando non sovrascrive è uguale.

È possibile?

Modifica: A proposito, dovrò anche forzare l'override di hashcode ...

È stato utile?

Soluzione

No, non puoi. Quello che puoi fare, tuttavia, è usare una classe base astratta invece di un'interfaccia e rendere equals () abstract:

abstract class Command {
   // put other methods from Command interface here

   public abstract boolean equals(Object other);
   public abstract int hashCode();
}

Le sottoclassi di Command devono quindi fornire i propri metodi uguali e hashCode.

In genere è una cattiva pratica forzare gli utenti API ad estendere una classe base, ma in questo caso può essere giustificato. Inoltre, se rendi Command una classe di base astratta anziché un'interfaccia, anziché introdurre una classe di base artificiale in aggiunta all'interfaccia di comando, allora non c'è alcun rischio per la tua API gli utenti sbagliano.

Altri suggerimenti

Puoi estendere i tuoi oggetti da un XObject astratto anziché da java.lang.Object?

public abstract class XObject
 extends Object
{
@Override
public abstract boolean equals(Object o);
}

Le classi astratte non funzioneranno se hai un nipote poiché suo padre ha già ignorato entrambi i metodi egual e hashCode e quindi hai di nuovo il tuo problema.

Prova a utilizzare annotatins e APT ( http: // docs.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html ) per farlo.

Ciò sarebbe possibile solo se Command fosse un'interfaccia o una classe astratta, dove equals (..) è un metodo dichiarato astratto.

Il problema è che l'Oggetto, che è la superclasse di tutti gli oggetti, definisce già questo metodo.

Se si desidera indicare che si tratta di un problema (in fase di esecuzione), è possibile generare un'eccezione per forzare gli utenti dell'API a sovrascriverlo. Ma non è possibile al momento della compilazione, almeno per quanto ne so.

Prova a aggirarlo, disponendo di un metodo specifico per l'API, ad es. CommandEquals. L'altra opzione è (come detto) estendere un'altra classe che definisce un metodo astratto uguale a.

Puoi creare booleani myEquals () in Command interfaccia , e creare Adapter in questo modo:

class MyAdapter{
  Command c;
  boolean equals(Object x) {
    return c.myEquals((Command)x);
  }
}

Quindi basta usare map.put (chiave, nuovo MyAdapter (comando)) invece di map.put (chiave, comando)

Bene, se vuoi un controllo di runtime puoi fare qualcosa del tipo:

    interface Foo{

}
class A implements Foo {

}
class B implements Foo {
    @Override
    public boolean equals(Object obj) {
        return super.equals(obj);
    }
}

public static void main(String[] args) {
    Class<A> clazzA = A.class;
    Class<B> clazzB = B.class;

    Class<Object> objectClass = Object.class;
    try {
        Method methodFromObject = objectClass.getMethod("equals",Object.class);
        Method methodFromA = clazzA.getMethod("equals",Object.class);
        Method methodFromB = clazzB.getMethod("equals",Object.class);
        System.out.println("Object == A" + methodFromObject.equals(methodFromA));
        System.out.println("Object == B" + methodFromObject.equals(methodFromB));
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
}

Stampa true per il primo e false per il secondo. Se lo desideri, il tempo di compilazione sembra che l'unica opzione sia quella di creare un'annotazione e utilizzare lo strumento di elaborazione delle annotazioni per verificare che tutte le classi annotate abbiano la precedenza su uguale.

interface A{
    public boolean equal2(Object obj);
}

abstract class B implements A {

    @Override
    public boolean equals(Object obj) {
        return equal2(obj);
    }

}


class C extends B {

    public boolean equal2(Object obj) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}

Dato che equals () è ereditato da Object dubito che non si possa davvero forzarlo, poiché per ogni c'è un automatico implementazione ereditata di equals () disponibile.

Non penso che sia possibile forzare l'override degli uguali come viene dalla classe Object.

In una nota correlata, nota che devi sovrascrivere il metodo 'hashCode' dalla classe Object ogni volta che esegui l'override uguale. Questo diventa particolarmente importante se intendi utilizzare le istanze delle tue classi come chiavi di una mappa. Controlla questo articolo: http://www.artima.com/lejava/articles/equality.html che fornisce alcuni suggerimenti su come sovrascrivere gli uguali in modo corretto

Come hanno già spiegato le altre risposte, no non puoi forzare il tipo di cosa che stai cercando di fare.

Una cosa che potrebbe funzionare 'abbastanza' è definire una seconda interfaccia, chiamarla MappableCommand.

public interface MappableCommand 
{

}

Nella tua documentazione per questa interfaccia indica che una classe dovrebbe implementare questa interfaccia (vuota) solo se il progettista della classe ha considerato i requisiti che hai dichiarato.

Quindi puoi impostare il Tipo valore della tua mappa su MappableCommand e solo i MappableCommands potranno essere aggiunti alla mappa.

È simile alla logica alla base della necessità di implementare l'interfaccia (vuota) serializzabile per le classi che possono essere serializzate dai meccanismi di serializzazione predefiniti di Java.

Se non funziona, potrebbe essere necessario accontentarsi di lanciare un errore di runtime;

Modifica falsa:

Se volessi rendere questo requisito più ovvio, potresti definire la nuova interfaccia in questo modo

public interface MappableCommand 
{

    public void iOverrodeTheEqualsMethod();

    public void seriouslyIPromiseThatIOverrodeIt();

}

Ecco una variante di alcune delle altre soluzioni proposte:

public abstract class CommandOverridingEquals implements Command {
    public abstract boolean equals(Object other);
    public abstract int hashcode();
}

Map<String, CommandOverridingEquals> map = 
    new HashMap<String, CommandOverridingEquals>();

O se vuoi davvero esserne sicuro, usa una hashmap selezionata; per es.

Map<String, CommandOverridingEquals> map = Collections.checkedMap(
    new HashMap<String, Command>(),
    String.class, CommandOverridingEquals.class);

Ma qualunque cosa tu faccia, non puoi impedire a qualcuno di farlo:

public class AntiFascistCommand extends CommandOverridingEquals {
    public boolean equals(Object other) { return super.equals(other); }
    public int hashcode() { return super.hashcode(); }
    ...
}

Tendo a pensare che questo genere di cose causerà problemi in pista. Ad esempio, supponiamo di avere un sacco di classi di comandi esistenti che estendono alcune altre classi di base e (per inciso) sovrascrivono equals e hashcode nel modo prescritto. Il problema è che non posso usare quelle classi. Invece, sono costretto a reimplementarli o scrivere un sacco di involucri.

IMO, è una cattiva idea provare a forzare lo sviluppatore in un particolare modello di implementazione. Sarebbe meglio inserire alcuni avvisi forti nei Javadocs e fare affidamento sugli sviluppatori per fare la cosa giusta .

È possibile fornire il proprio java.util.comparator alla mappa in questione?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top