Question Avertissement Findbugs: La méthode Equals ne doit rien présumer du type de son argument


Lors de l'exécution de FindBugs sur mon projet, j'ai eu quelques cas d'erreur décrits ci-dessus.

À savoir, mes versions de substitution d'équals convertissent l'objet RHS dans le même type que l'objet dans lequel la version de substitution est définie.

Cependant, je ne sais pas si une meilleure conception est possible car AFAIK Java n'autorise pas la variance des paramètres de la méthode. Il est donc impossible de définir un autre type pour le paramètre égal.

Est-ce que je fais quelque chose de très mal ou est-ce que FindBugs est trop impatient?

Une autre façon de formuler cette question est la suivante: quel est le comportement correct si l'objet transmis à equals n'est pas du même type qu'un objet LHS? S'agit-il d'un faux ou doit-il y avoir une exception?

Par exemple:

public boolean equals(Object rhs)
{
    MyType rhsMyType = (MyType)rhs; // Should throw exception
    if(this.field1().equals(rhsMyType.field1())... // Or whatever
}

12
2017-12-12 23:04


origine


Réponses:


Généralement, lors de la mise en œuvre égale, vous pouvez vérifier si la classe de l'argument est égale (ou compatible) à la classe d'implémentation avant de la lancer. Quelque chose comme ça:

if (getClass() != obj.getClass())
    return false;
MyObj myObj = (MyObj) obj;

Faire de cette façon empêchera l'avertissement FindBugs.

Une note latérale pour répondre à un commentaire:
Certaines personnes prétendent utiliser instanceof au lieu de getClass vérifier la sécurité de type. Il y a un grand débat à ce sujet, que je n'essayais pas de faire quand j'ai noté que vous pouvez vérifier l'égalité de classe ou compatibilité, mais je suppose que je ne peux pas y échapper. Cela se résume à ceci - si vous utilisez instanceof vous pouvez prendre en charge l'égalité entre les instances d'une classe et les instances de sa sous-classe, mais vous risquez de rompre le contrat symétrique de equals. Je recommande généralement de ne pas utiliser instanceof à moins que vous sachiez que vous en avez besoin et que vous savez ce que vous faites. Pour plus d'informations, voir:


29
2017-12-12 23:10



Vous faites probablement quelque chose comme ça:

public class Foo {
  // some code

  public void equals(Object o) {
    Foo other = (Foo) o;
    // the real equals code
  }
}

Dans cet exemple, vous supposez quelque chose à propos de l'argument de equals (): Vous supposez qu'il s'agit du type Foo. Cela ne doit pas être le cas! Vous pouvez également obtenir une chaîne de caractères (auquel cas vous devriez presque retourner la valeur false).

Donc, votre code devrait ressembler à ceci:

public void equals(Object o) {
  if (!(o instanceof Foo)) {
    return false;
  }
  Foo other = (Foo) o;
  // the real equals code
}

(ou utiliser le plus strict getClass() != o.getClass() mentionné par Dave L.

Vous pouvez également le regarder de cette façon:

Integer i = new Integer(42);
String s = "fourtytwo";
boolean b = i.equals(s);

Y a-t-il une raison pour que ce code lance un ClassCastException au lieu de finir normalement et de mettre b à false?

Jeter un ClassCastException en réponse à .equals() ne serait pas raisonnable. Parce que même si c’est une question stupide ("Bien sûr, une chaîne n’est jamais égale à un Foo!"), Il s’agit toujours d’une question valide avec une réponse parfaitement correcte ("non" == false).


7
2017-12-12 23:15



Je recommande d'ignorer cet avertissement findbugs. En pratique, si equals est appelé avec un objet d'une classe inattendue, il s'agit presque certainement d'un bogue, et vous voulez rapidement échouer avec les bogues.

Par exemple, si vous avez un "fichiers ArrayList" et que vous appelez files.contains ("MyFile.txt"), ce serait bien si vous aviez une exception ClassCastException. Au lieu de cela, Java renvoie simplement la valeur false, et cela prend probablement beaucoup de temps avant que vous ne découvriez ce bogue.


1
2018-06-10 15:09



Je commence mes implémentations égales (Object) comme ceci:

if ((object == null) || !(object instaceof ThisClass)) {
    return false;
}

Cela empêchera également l'avertissement FindBugs mais ne retournera pas automatiquement false lorsqu'une sous-classe de ThisClass est en cours de transmission. Elle pourrait également être considérée comme égale, surtout si son equals(Object) la méthode n'a pas été remplacée.


0
2017-12-12 23:17