Domanda

Sto cercando di ignorare il metodo equals per una classe con parametri.

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (!(obj instanceof Tuple))
        return false;

    Tuple<E> other = (Tuple<E>) obj; //unchecked cast
    if (!a0.equals(other.a0) && !a0.equals(other.a1)) {
        return false;
    }
    if (!a1.equals(other.a1) && !a1.equals(other.a0)) {
        return false;
    }

    return true;
}

Come posso assicurarmi che < E > dell'oggetto altro sia uguale a this ?

È stato utile?

Soluzione

Puoi farlo mantenendo un riferimento al tipo Class < E > . Tuttavia, a mio avviso, i test di uguaglianza dovrebbero riguardare i valori rappresentati dagli oggetti piuttosto che i tipi concreti che i valori vengono espressi .

Un esempio classico di questo è l'API Collections, ad esempio. new ArrayList < String > (). equals (new LinkedList < Object > ()) restituisce true . Sebbene abbiano tipi completamente diversi, rappresentano lo stesso valore, vale a dire "una raccolta vuota".

Personalmente, due Tuple che rappresentano gli stessi dati (es. (" a " ;, " b ") ) non dovrebbero essere uguali, perché uno è di digita Tuple < String > mentre l'altro è Tuple < Object > ?

Altri suggerimenti

A causa della cancellazione non puoi. Il meglio che puoi fare è memorizzare nella classe tupla il tipo che prevedi che la Tupla possa contenere in una "quotazione java.lang.Class". membro del campo. Quindi puoi confrontare questi campi per assicurarti che la classe tupla abbia gli stessi tipi.

Vedi anche questa discussione: Qual è l'equivalente della coppia C ++ < L , R > in Java?

Sarebbe utile se pubblichi di più sulla tua classe. Sto pensando che il cast non controllato e il tuo numero di campi che equiparli significhino che dovrebbe essere Tuple & E, F & GT; no?

EDIT: ecco un'utile classe Pair che uso regolarmente (puoi adattare la tua classe Tuple se necessario). Nota, simile ai suggerimenti di altri in questa classe, consente ai membri contenuti di decidere la questione dell'uguaglianza. Il tuo caso d'uso è ciò che dovrebbe determinare se l'uguaglianza è realmente basata sul tipo di membri contenuti.

/**
 * Adapted from http://forums.sun.com/thread.jspa?threadID=5132045
 * 
 * 
 * @author Tim Harsch
 *
 * @param <L>
 * @param <R>
 */
public class Pair<L, R> {

    private final L left;
    private final R right;

    public R getRight() {
        return right;
    } // end getter

    public L getLeft() {
        return left;
    } // end getter

    public Pair(final L left, final R right) {
        this.left = left;
        this.right = right;
    } // end constructor

    public static <A, B> Pair<A, B> create(A left, B right) {
        return new Pair<A, B>(left, right);
    } // end factory method

    @Override
    public final boolean equals(Object o) {
        if (!(o instanceof Pair<?,?>))
            return false;

        final Pair<?, ?> other = (Pair<?, ?>) o;
        return equal(getLeft(), other.getLeft()) && equal(getRight(), other.getRight());
    } // end method

    public static final boolean equal(Object o1, Object o2) {
        if (o1 == null) {
            return o2 == null;
        }
        return o1.equals(o2);
    } // end method

    @Override
    public int hashCode() {
        int hLeft = getLeft() == null ? 0 : getLeft().hashCode();
        int hRight = getRight() == null ? 0 : getRight().hashCode();

        return hLeft + (37 * hRight);
    } // end method

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append('<');
        if( left == null ) {
            sb.append("null");
        } else {
            sb.append(left.toString());
        } // end if
        sb.append(',');
        if( right == null ) {
            sb.append("null");
        } else {
            sb.append(right.toString());
        } // end if
        sb.append('>');
        return sb.toString();
    } // end method
} // end class

Mi sono appena imbattuto in questo problema, e nel mio caso -particolare non avevo bisogno di conoscere il tipo E.

Ad esempio:

public class Example<E> {
    E value;

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Example<?> other = (Example<?>) obj;
        if (value == null) {
            if (other.value != null)
                return false;
        } else if (!value.equals(other.value))
            return false;
        return true;
    }
}

Nel codice sopra, non c'è cast non controllato a causa dell'utilizzo di Esempio <? > . Il carattere jolly parametro '?' salva il giorno.

Purtroppo, non puoi farlo al momento della compilazione; l'informazione è sparita. Tali sono le conseguenze della cancellazione del tipo . Un'alternativa è quella di memorizzare il parametro come istanza Class , quindi cercarlo in seguito.

Poiché i generici vengono cancellati al momento della compilazione , tu praticamente no. In fase di esecuzione, qualsiasi parametro di tipo è sparito e, per quanto riguarda la JVM, sono esattamente gli stessi sotto tutti gli aspetti.

Il modo per aggirare il problema consiste nel memorizzare un campo Class che rappresenta il tipo e creare l'oggetto con quel tipo nel costruttore.

Un costruttore di esempio:

public class Tuple < E > {

    public Tuple(Class<E> c) {
        //store the class
    }

}

O potresti usare una fabbrica:

public static <E> Tuple <E> getTuple(Class<E> type) {
    // Create and return the tuple, 
    // just store that type variable in it 
    // for future use in equals.
}

I suggerimenti per conservare un riferimento al tipo di E con un oggetto Class sembrano inefficienti (sono molti riferimenti inutili a un Class che occupa memoria) e inutile per il tuo problema.

Non è generalmente vero che Foo < Bar > e Foo < Baz > dovrebbero essere ineguali. Quindi non hai bisogno di E . E, nel codice che hai scritto, non ti serve nemmeno per la compilazione. Basta eseguire il cast su Tuple <? & Gt; , poiché a quel punto è davvero tutto ciò che sai di Tuple . Compila ancora e tutto.

Se ti viene passato Tuple con dati di due tipi selvaggiamente diversi, tali elementi non saranno uguali e il tuo metodo restituirà false , come desiderato. (Si spera - dipende da quei tipi che implementano equals in modo corretto.)

Off-topic - ti rendi conto che secondo la tua implementazione, Tuple (a0, a1) è uguale a Tuple (a1, a1)? Sospetto che non sia quello che vuoi ...

Sull'argomento, come altri hanno già detto, la cancellazione lo rende impossibile. Ma dovresti riconsiderare il motivo per cui lo desideri: il controllo dell'uguaglianza avviene solo in fase di esecuzione e i generici sono solo in fase di compilazione . Concettualmente, una variabile ha parametri generici, ma un oggetto no. Pertanto, quando si confronta l'uguaglianza degli oggetti, i parametri generici non contano affatto; non puoi comunque intraprendere alcuna azione appropriata in base a loro in fase di esecuzione.

L'uguaglianza degli oggetti e la co- / contro-varianza dei parametri generici sono due preoccupazioni ortogonali.

Sono d'accordo con i commenti sopra, perché la classe E deve essere uguale? e come si desidera trattare le sottoclassi di E?

Comunque, dato che qui c'è un esempio di codice che può aiutarti:

public class Example<T> {

  T t;

  public Example(T t) {
    this.t = t;
  }

  public static void main(String[] args) {
    final String s = "string";
    final Integer i = 1;
    final Number n = 1;

    final Example<String> exampleString = new Example<String>(s);
    final Example<Integer> exampleInteger = new Example<Integer>(i);
    final Example<Number> exampleNumber = new Example<Number>(n);

    System.out.println("exampleString subclass "  + exampleString.t.getClass());
    System.out.println("exmapleIntger subclass " + exampleInteger.t.getClass());
    System.out.println("exmapleNumber subclass " + exampleNumber.t.getClass());
    System.out.println("Integer equals Number = " + 
        exampleInteger.t.equals(exampleNumber.t));
  }
}

Puoi chiamare t.getClass () per ottenere informazioni sulla classe sul tipo di T (supponendo che non sia nullo, ovviamente.)

Spero che questo aiuti.

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