public boolean equals(ClassName other) { // 잘못된 equals!! } // 다음이 올바르다. 무조건 다음과 같이한다. public boolean equals(Object other) { ... }
equals
가 사용하는 필드와 hashCode
가 사용하는 필드를 동일하게 맞춰야 한다.HashSet.contains
등이 올바르게 작동하지 않게 된다. HashSet은 hashCode 기반으로 bucket을 결정한다. equals(Object)
호출시 true
라면 두 객체의 hashCode()
는 동일한 int 를 리턴해야 한다.hashCode
는 equals
에서 사용한 필드들만을 사용해야한다.equals
, hashCode
를 구현한 상태에서 해당 필드 값을 변경하면 동일 객체라도 hashCode가 바뀌게 된다.
equals
는 null
이 아닌 객체간에는 항상 동치 관계를 유지하게 구현해야만 한다.
x.equals(x)
는 항상 true
여야 한다.x.equals(y)
가 true
이고 y.equals(z)
가 true
이면 x.equals(z)
도 true
여야 한다. 이행성 위반 예x.equals(y)
를 여러번 호출해도 일관성있게 true
를 반환하거나 일관성있게 false
를 반환해야 한다.x.equals(null)
은 항상 false
를 반환해야 한다.
상속 관계에서는 위 동치성을 쉽게 위반하게 된다. canEqual
메소드를 통해 이를 지켜내야 한다.
equals
와 hashCode
를 override 할 때마다 can equal
메소드도 함께 구현하고 오버라이드 해주면 된다.
이를 통해 현재 클래스의 상위클래스와 동등하게 평가되는 것을 막을 수 있다.
// Point 라는 클래스가 존재할 때 public boolean canEqual(Object other) { return (other instanceof Point); } @Override public boolean equals(Object other) { boolean result = false; if (other instanceof Point) { Point that = (Point) other; // that.canEqual(this) 가 핵심이다. 이를 거꾸로 this.canEqual(that) 으로 사용하면 안된다. result = (that.canEqual(this) && this.getX() == that.getX() && this.getY() == that.getY()); } return result; } // 오버라이드 되는 ColoredPoint 클래스에서는 @Override public boolean equals(Object other) { boolean result = false; if (other instanceof ColoredPoint) { ColoredPoint that = (ColoredPoint) other; result = (that.canEqual(this) && this.color.equals(that.color) && super.equals(that)); } return result; } // that.canEqual(this) 호출을 통해서 상위클래스(Point)는 결코 ColoredPoint와 같을 수 없게 보장됨. @Override public boolean canEqual(Object other) { return (other instanceof ColoredPoint); }
canEqual
이 구현되어 있는 경우 하위 클래스에서 canEqual
을 구현하거나 하지 않음으로써 상위클래스와의 동등성 비교를 허용하거나 안할 수 있다.false
가 될 가능성이 높은 잘못된 코딩이다.