Capitulo 3 Metodos comuns a todos os objetosItem 8 Obedecendo ocontrato do equals Sempre que instancias de uma classe precisaremser comparadas entre si e houver a nocao de identidade que difere da meraidentidade entre objetos (ou seja, aquela que retorna verdadeiro meramente sedois objetos forem iguais na memoria), o metodo equals deve serimplementado. Deve-se obedecer as seguintes regras (contrato)ao sobreescrever o equals:o Reflexividade: Dado x, x.equals(x) deve retornartrue.o Simetria: Dado x e y, x.equals(y) deve retornartrue se e somente se y.equals(x) retornar true.o Transitividade:Dado x,y e z. Se x.equals(y) e true e y.equals(z) e true, entao x.equals(z)deve ser true. Transitividade: Essa caracteristica merece umaatencao especial. A discussao que segue e longa e complicada, mas acheiimportante acrescentar aqui. Numarelacao de heranca, nada impede que uma subclasse acrescente atributos quedevem ser considerados na execucao do equals. Isso gera inumeros problemas!Vejamos o codigo abaixo: public class Point { private final int x; private final int y; public Point(int x, int y) {this.x = x;this.y = y;}@Override public boolean equals(Object o) { if (!(o instanceof Point)) return false; Point p = (Point)o; return p.x == x && p.y == y;}... // Remainder omitted} public class ColorPoint extends Point {private final Color color;public ColorPoint(int x, int y, Color color) {super(x, y);this.color = color;}... // Remainder omitted} O funcionamentoque esperamos e que consigamos comparar intercaladamente pontos e pontoscoloridos atraves do metodo equals. Afinal, sao todos pontos. Mas como ColorPoint acrescentou umainformacao a mais a sua identidade, esta deve ser considerada. Como implementaro equals para a classe ColorPoint? Vejamos algumas solucoes. 1 - Comparar apenas objetosda mesma classe: // Broken - violates symmetry!@Override public boolean equals(Object o) {if (!(o instanceof ColorPoint))return false;return super.equals(o)&& ((ColorPoint) o).color == color;}Essecodigo viola a simetria. Point.equals(ColorPoint) retornara true se ascoordenadas forem as mesmas mas ColorPoint.equals(Point) sempre retornarafalse. 2 Naoconsiderar informacao de cor em comparacoes misturadasResolve oproblema da simetria mas ao custo da transitividade// Broken - violates transitivity!@Override public boolean equals(Object o) {if (!(o instanceof Point))return false; if (!(o instanceof ColorPoint))return o.equals(this); //nao considera cor se Object for Point return super.equals(o) &&((ColorPoint) o).color == color;}ColorPoint p1 = new ColorPoint(1, 2,Color.RED);Point p2 = new Point(1, 2);ColorPoint p3 = new ColorPoint(1, 2,Color.BLUE);P1.equals(P2) retorna true = > chama oequals do ColorPoint na linha return o.equals(this)P2.equals(P3) retorna true => chama oequals do PointP1.equals(P3) retorna false => retornafalso pois sao ColorPoint de cores diferentes3 Usando o getClass ao inves doinstanceOf// Broken - violates Liskov substitutionprincipl@Override public boolean equals(Object o) { if (o== null || o.getClass() != getClass()) return false; Pointp = (Point) o; return p.x == x && p.y == y;}O codigoacima compara apenas objetos da mesma classe. O principio da substituicao deLiskov diz que qualquer propriedade importante para um tipo deve valer tambempara o subtipo. Assim, qualquer metodo escrito para um tipo, deve funcionarcorretamente nos seus subtipos. A implementacao do equals, na classe Point, naforma acima, viola esta propriedade. Qualquer subclasse criada a partir dePoint e que precisar (e puder) usar o equals da classe Point, nao vaifuncionar. Sempre que esta nova classe for comparada com a classe Point iraretornar false, independentemente se a comparacao dos atributos estivervalida. Nao