2013-05-05 5 views
16

Ich habe eine Klasse, die eine generischen Typ akzeptiert, und ich möchte die equals Methode in einer nicht umständlichen Weise überschreiben (d. H. Etwas, das sauber aussieht und eine minimale Menge an Code, aber für einen sehr allgemeinen Anwendungsfall).Wie implementiert man "gleich" -Methode für Generika mit "instanceof"?

Im Moment habe ich so etwas wie diese:

public class SingularNode<T> { 
    private T value; 

    @SuppressWarnings("unchecked") 
    @Override 
    public boolean equals(Object other){ 
     if(other instanceof SingularNode<?>){ 
      if(((SingularNode<T>)other).value.equals(value)){ 
       return true; 
      } 
     } 
     return false; 
    } 
} 

Which, ich bin zu raten, ist ziemlich fehlerhaft - ich bin eine Besetzung zu SingularNode<T> auf dem other Objekt zu tun, was könnte möglicherweise einen Fehler aus,.

Eine andere Sache ist - wenn ich if(other instanceof SingularNode<?>) mache, überprüfe ich eigentlich nicht genau das Richtige. Ich möchte tatsächlich gegen den Typ T und nicht Typ ? überprüfen. Jedes Mal, wenn ich versuche, die ? in T zu machen, bekomme ich einige Fehler wie:

Kann nicht instanceof prüfen gegen parametrisierte Typ SingularNode<T> auszuführen. Verwenden Sie das Formular SingularNode<?> statt, da eine weitere generische Typinformationen zur Laufzeit gelöscht werden

Wie kann ich dieses Problem umgehen? Gibt es eine Möglichkeit zu tun?

Ich nehme an gibt es eine wirklich hässliche Hack-Lösung wie folgt aus:

@SuppressWarnings("unchecked") 
public boolean isEqualTo(Class<?> c, Object obj){ 
    if(c.isInstance(obj) && c.isInstance(this)){ 
     if(((SingularNode<T>)obj).value.equals(value)){ 
      return true; 
     } 
    } 
    return false; 
} 

Aber das sieht einfach wirklich umständlich mit dem zusätzlichen Verfahrensparameter, und es ist auch keine eingebaute Funktion wie equals ist.

Jeder, der Generika verstehen, bitte erklären Sie dies? Ich bin nicht so gut mit Java vertraut, wie Sie deutlich sehen können, also erklären Sie bitte mit ein bisschen mehr Detail!

+2

Der Fehler, den Sie haben, ist, weil sowieso die generischen Typen gelöscht werden. Ich denke, das "T" sollte sich um die Überprüfung kümmern, ob das bestandene Element der gleichen Klasse angehört. –

+0

@MichalBorek mmm Könntest du bitte ein wenig ausarbeiten? Ich verstehe nicht ganz. Also in einer anderen Klasse, wenn ich folgendes mache: 'new SingularNode (5) .equals (neuer SingularNode ('k'));' Wissen Sie zufällig, wo die Überprüfung stattfindet? –

+0

Ich habe eine Antwort hinzugefügt, um eine Menge Code zu schreiben. –

Antwort

17

Diese Version gibt keine Warnungen

public boolean equals(Object other){ 
    if (other instanceof SingularNode<?>){ 
     if (((SingularNode<?>)other).value.equals(value)){ 
      return true; 
     } 
    } 
    return false; 
} 

Was zu SingularNode<T> Gießen es nichts hilft, kann man nicht davon ausgehen, dass T alles andere als Object sein kann.

Erfahren Sie mehr darüber, wie Generika in Java kompiliert werden bei

https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

+0

Danke für die Antwort. Ja, dieser Code gibt keine Warnung aus, weil Sie "(SingularNode ) andere" bis zum Typ "?" Statt "T" eingeben.Allerdings würde ich nur Instanzen vom Typ "T" verwenden, wenn ich in Zukunft entscheiden würde, dass ich etwas Besonderes mit dem Typ "T" machen möchte. wenn das Sinn macht? –

+0

Ich denke nicht, dass es Sinn macht, da Sie nicht davon ausgehen können, dass T nichts anderes als Objekt ist, denn es kann alles sein. Möglicherweise könnte es anders sein im Falle von T erweitert etwas –

+0

ah okay. Also ist eigentlich '?' So ziemlich wie 'T' hier? –

1

ich hier beantworten Code zu setzen ..

In Ihrem Beispiel haben Sie (in Pseudo-Code) Integer(5).equals(Char('k')) die false ist nach folgenden gleich Implementierung auf java.lang.Integer:

public boolean equals(Object obj) { 
    if (obj instanceof Integer) { 
     return value == ((Integer)obj).intValue(); 
    } 
    return false; 
} 

geht auf diese Weise Sie müssen sich keine Gedanken über das Casting machen.

+0

Oh okay, ich sehe was du jetzt meinst. Danke. Aus irgendeinem Grund hatte ich den Eindruck, dass Java eine 'InvalidClassCast-Exception' oder etwas anderes wirft, wenn Sie versuchen,' Character' mit einem 'Integer' zu werfen. aber es gibt nur falsch statt Fehler zurück. Ich sehe es jetzt –

+0

Normalerweise denken die ersten, die wir einchecken, ist gleich Methode ist die Klasse des anderen Objekts, und alle Java entspricht Implementierungen tun es so. –

+0

Also in der Besetzung '(SingularNode ) obj', was tatsächlich passiert, ist, dass es' obj' zu 'SingularNode' statt" T "wirft. was bedeutet, dass der 'obj.value' tatsächlich Typ' T' ist, oder? –

2

Evgeniy's solution und Michal's reasoning korrekt sind - Sie müssen nicht über die Art der T hier zu kümmern. Der Grund dafür ist, dass die equals-Methode nicht davon abhängig ist, dass Generika korrekt funktionieren. Stattdessen wird es von Object deklariert und es dauert eine Object. Daher ist es verantwortlich für die Überprüfung des Laufzeittypen von was auch immer in übergeben wurde.

Wenn thisSingularNode<String> passiert zu sein und Sie vergleichen es mit einem SingularNode<Integer>, dann ist ((SingularNode<?>)other).value.equals(value) völlig in Ordnung, weil Integer.equals mit einem String Argumente Aufruf richtig false zurück.

-4

Sie müssen kein Casting verwenden. besten gleich zu Implementierung Ich mag dieses sehen

@Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (!(o instanceof VehicleModel)) return false; 

     VehicleModel that = (VehicleModel) o; 

     if (vehicleName != null ? !vehicleName.equals(that.vehicleName) : that.vehicleName != null) 
      return false; 

     return true; 
    } 
+3

Ich bin verwirrt. Ich sehe Casting. –

0

ich das gleiche Problem haben, aber es ist allgemeiner. Ich habe eine Klasse, in der ich 3 generische Typen habe. Ich muss keine Variablen dieser Typen speichern, da diese Klasse für die Transformation verwendet wird. Es gibt jedoch "request" Variablen und ich verwende Cache basierend auf dieser Klasse, also muss ich die equals() Methode implementieren, die auf diesen Generics basiert.

Wissen Sie, ob es einen Ansatz gibt, wie man es ohne Reflektion macht? Vielleicht interne Variable dieses Typs .. Allerdings ist es dann null.

public class TheClass<I, O, M> { 

    private ClassA param1; 
    private ClassB param2; 
    private ClassC<M> param3; 
    private BiFunction<ClassC<M>, I, Optional<O>> mapping; 

    public ClassD<O, M> doSomething(ClassD<I, M> param) { 
     ... 
    } 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) { 
      return true; 
     } 
     if (o == null) { 
      return false; 
     } 
     if (getClass() != o.getClass()) { 
      return false; 
     } 
     TheClass<?, ?, ?> that = (TheClass<?, ?, ?>) o; 

     return Objects.equals(getParam1(), that.getParam1()) && 
       Objects.equals(getParam2(), that.getParam2()) && 
       Objects.equals(getParam3(), that.getParam3()); 
    } 
} 

Für eine bessere Vorstellung ... Ich habe eine Reihe von DAO-Objekten, die Daten aus der Datenbank bekommen. Auf der anderen Seite haben wir eine andere Gruppe von API-Providern, die ähnliche Daten in verschiedenen Formaten (REST, interne Systeme ...) zur Verfügung stellen. Wir benötigen eine Zuordnungsfunktion von einem Typ zum anderen. Wir verwenden Caching für bessere Leistung und der einzige Man-in-the-Middle ist diese Klasse.

Verwandte Themen