ITEM 10: The gerneral contract when overriding equlas
equals
๋ฉ์๋์ ์ฌ์ ์์๋ ์ฌ๋ฌ๊ฐ์ง ํจ์ ์ด ์๋ค. ๊ทธ๋ฌ๋ฏ๋ก ๋ค์๊ณผ ๊ฐ์ ์ํฉ์์๋ ์ฌ์ ์ ํ์ง ์๋ ๊ฒ์ด ์ข๋ค.
๊ฐ ์ธ์คํด์ค๊ฐ ๋ณธ์ง์ ์ผ๋ก ๊ณ ์ ํ ๊ฒฝ์ฐ( ๋์ํ๋ ๊ฐ์ฒด๋ฅผ ํํํ๋ ํด๋์ค ex-
Thread
)์ธ์คํด์ค์ ๋ ผ๋ฆฌ์ ๋์น์ฑ(logical equality)์ ๊ฒ์ฌํ ์ผ์ด ์๋ ๊ฒฝ์ฐ
์์ ํด๋์ค์์ ์ฌ์ ์ํ
equals
๊ฐ ํ์ ํด๋์ค์๋ ์ ํฉํ ๊ฒฝ์ฐ(AbstractSet
,AbstractList
)ํด๋์ค๊ฐ private or package-private์ด๊ณ
equals
๋ฉ์๋๋ฅผ ํธ์ถํ ์ผ์ด ์๋ ๊ฒฝ์ฐ
๊ทธ๋ ๋ค๋ฉด equals
๋ฅผ ์ฌ์ ์ํด์ผํ ๋๋ ์ธ์ ์ผ๊น?
๊ฐ์ฒด ์๋ณ์ฑ(๋ ๊ฐ์ฒด๊ฐ ๋ฌผ๋ฆฌ์ ์ผ๋ก ๊ฐ์์ง)์ด ์๋๋ผ ๋
ผ๋ฆฌ์ ๋์น์ฑ(logical equality)์ ํ์ธํด์ผํ์ง๋ง, ์์ ํด๋์ค์ equals
๊ฐ ์ฌ์ ์๋์ง ์์ ๊ฒฝ์ฐ์ด๋ค. ์ฃผ๋ก, ๊ฐ ํด๋์ค(Integer
, String
)๊ฐ ํด๋น๋๋ค.
๊ฐ ํด๋์ค์ด๋๋ผ๋ ์ ์ ํฉํฐ๋ฆฌ ๋ฉ์๋ - item1๋ผ๋ฉด equals
๋ฅผ ์ฌ์ ์ํ์ง ์์๋ ๋๋ค. (Enum
ํฌํจ)
equals ๋ฉ์๋ ๊ท์ฝ
equals
๋ฉ์๋๋ ๋์น๊ด๊ณ(equivalence relation)๋ฅผ ๊ตฌํํ๋ฉฐ, ๋ค์์ ๋ง์กฑํ๋ค.
๊ท์ฝ
์ค๋ช
๋ฐ์ฌ์ฑ(reflexivity)
null์ด ์๋ ๋ชจ๋ ์ฐธ์กฐ ๊ฐ x์ ๋ํด, x.equals(x)
๋ true์ด๋ค.
๋์น์ฑ(symmetry)
null์ด ์๋ ๋ชจ๋ ์ฐธ์กฐ ๊ฐ x,y์ ๋ํด, x.equals(y)
๊ฐ true์ด๋ฉด, y.equals(x)
๋ true์ด๋ค.
์ถ์ด์ฑ(transitivity)
null์ด ์๋ ๋ชจ๋ ์ฐธ์กฐ ๊ฐ x, y, z์ ๋ํด x.equals(y)
๊ฐ true์ด๊ณ y.equals(z)
๊ฐ true์ด๋ฉด, x.eqauls(z)
๋ true์ด๋ค.
์ผ๊ด์ฑ(consistency)
null์ด ์๋ ๋ชจ๋ ์ฐธ์กฐ ๊ฐ x,y์ ๋ํด x.equals(y)
๋ฅผ ๋ฐ๋ณตํด์ ํธ์ถํ๋ฉด ํญ์ true or flase๋ฅผ ๋ฐํํ๋ค.
null ์๋
null์ด ์๋ ๋ชจ๋ ์ฐธ์กฐ ๊ฐ x์ ๋ํด, x.equals
๋ flase์ด๋ค.
equals
๊ท์ฝ์ ์ด๊ธฐ๋ฉด ๊ทธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๋ค๋ฅธ ๊ฐ์ฒด๋ค์ด ์ด๋ป๊ฒ ๋ฐ์ํ ์ง ์ ์ ์๋ค.
๋ฐ์ฌ์ฑ
๋จ์ํ ๋งํ๋ฉด ๊ฐ์ฒด๋ ์๊ธฐ ์์ ๊ณผ ๊ฐ์์ผ ํ๋ค๋ ๋ป์ด๋ค.
๋์นญ์ฑ
๋ ๊ฐ์ฒด๋ ์๋ก์ ๋ํ ๋์น์ฌ๋ถ์ ๋ํด ๋๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋์์ผํ๋ค.
public final class CaseInsensitiveString {
private final String s;
public CaseInsensitiveString(String s){
this.s = Objects.requireNonNull(s);
}
// ๋์นญ์ฑ ์๋ฐ
@Override
public boolean equals(Object o){
if(o instanceof CaseInsensitiveString)
return s.equalsIgnoreCase(((CaseInsensitiveString) o).s);
if(o instanceof String)
return s.equalsIgnoreCase((String) o);
return false;
}
}
CaseInsensitiveString cis = new CaseInsensitiveString("Test");
String s = "test";
System.out.println(s.equals(cis)); // false
System.out.println(cis.equals(s)); // true
์ฌ๊ธฐ์ ๋ฌธ์ ๋ String์ equals๋ CaseInsensitiveString
์ ์กด์ฌ๋ฅผ ๋ชจ๋ฅด๊ธฐ๋๋ฌธ์ false๋ฅผ ๋ฐํํ๋ฉฐ, ์ด๋ ๋์นญ์ฑ์ ์๋ฐํ๋ค.
equals ๊ท์ฝ์ ์ด๊ธฐ๋ฉด, ๊ทธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๋ค๋ฅธ ๊ฐ์ฒด๋ค์ด ์ด๋ป๊ฒ ๋ฐ์ํ ์ง ์ ์ ์๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ์๋์ ๊ฐ์ด String๊ณผ์ ์ฐ๋์ ํ๊ฒ ๋ค๋ ๋ชฉํ๋ฅผ ๋ฒ๋ ค์ผํ๋ค.
@Override
public boolean equals(Object o){
return o instanceof CaseInsensitiveString && ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}
CaseInsensitiveString cis = new CaseInsensitiveString("Test");
String s = "test";
System.out.println(s.equals(cis)); // false
System.out.println(cis.equals(s)); // false
์ถ์ด์ฑ
์ฒซ ๋ฒ์งธ ๊ฐ์ฒด์ ๋ ๋ฒ์งธ ๊ฐ์ฒด๊ฐ ๊ฐ๊ณ , ๋ ๋ฒ์งธ ๊ฐ์ฒด์ ์ธ ๋ฒ์งธ ๊ฐ์ฒด๊ฐ ๊ฐ๋ค๋ฉด, ์ฒซ๋ฒ์งธ ๊ฐ์ฒด์ ์ธ๋ฒ์งธ ๊ฐ์ฒด๋ ๊ฐ์์ผํ๋ค๋ ์๋ฏธ์ด๋ค.
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;
}
}
public class ColorPoint extends Point{
private final String color;
public ColorPoint(int x, int y, String color){
super(x,y);
this.color = color;
}
}
ColorPoint
ํด๋์ค์์ equals ๋ฉ์๋๋ฅผ ๊ตฌํํ์ง ์๋๋ค๋ฉด, Point
equals๊ฐ ์์๋์ด ์์ ์ ๋ณด๋ ๋ฌด์๋ ์ฑ ๋น๊ต๋ฅผ ์ํํ๊ฒ๋๋ค. ์ด๋, ๊ท์ฝ์ ์ด๊ธด ๊ฒ์ ์๋์ง๋ง, ์ค์ํ ์ ๋ณด๋ฅผ ๋์น๊ฒ ๋๋ฏ๋ก ๋ฐ์๋ค์ผ ์ ์๋ค.
@Override
public boolean equals(Object o){
if(!(o instanceof ColorPoint))
return false;
return super.equals(o) && ((ColorPoint) o).color == color;
}
๋ค์๊ณผ ๊ฐ์ด equals๋ฅผ ๊ตฌํํ๋ฉด Point์ ColorPoint๋ฅผ ๋น๊ตํ ๊ฒฐ๊ณผ์ ๊ทธ ๋์ ๋ฐ๊ฟ์ ๋น๊ตํ ๊ฒฐ๊ณผ๊ฐ ๋ค๋ฅผ ์ ์๋ค. ( ๋์นญ์ฑ ์๋ฐฐ )
Point p = new Point(1,2);
ColorPoint cp = new ColorPoint(1,2, Color.RED);
System.out.println(p.equals(cp)); // true
System.out.println(cp.equals(p)); // false
์ฐ์ p.equals(cp)
๋ ์์ ์ ๋ณด์ ๋ํ ๋น๊ต๋ ๋ฌด์ํ๊ณ , cp.equals(p)
๋ ๋งค๊ฐ๋ณ์์ ํด๋์ค ์ข
๋ฅ๊ฐ ๋ค๋ฅด๋ค๋ฉฐ ๋งค๋ฒ false๋ง ๋ฐํํ ๊ฒ์ด๋ค.
@Override
public boolean equals(Object o){
if(!(o instanceof Point))
return false;
if(!(o instanceof ColorPoint))
return o.equals(this);
return super.equals(o) && ((ColorPoint) o).color == color;
}
Point p = new Point(1,2);
ColorPoint cp = new ColorPoint(1,2, Color.RED);
ColorPoint cp2 = new ColorPoint(1,2,Color.BLACK);
System.out.println(cp.equals(p)); // true
System.out.println(p.equals(cp2)); // true
System.out.println(cp2.equals(cp)); // false
๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝํ๋ฉด, ๋์นญ์ฑ์ ๋ง์ง๋ง ์ถ์ด์ฑ์ ์ฌ์ ํ ์๋ฐฐ๋๋ฉฐ, ์ด ๋ฐฉ์์ ๋ฌดํ ์ฌ๊ท์ ๋น ์ง ์ํ๋ ์๋ค. ๊ตฌ์ฒด ํด๋์ค๋ฅผ ํ์ฅํด ์๋ก์ด ๊ฐ์ ์ถ๊ฐํ๋ฉด์ equals ๊ท์ฝ์ ๋ง์กฑ์ํฌ ๋ฐฉ๋ฒ์ ์กด์ฌํ์ง ์๋๋ค.
equals
์์ instance ๊ฒ์ฌ๋ฅผ getClass
๊ฒ์ฌ๋ก ๋ฐ๊พธ๋ฉด ๊ท์ฝ๋ ์งํค๋ฉด์ ์์์ด ๊ฐ๋ฅํ๋ค๊ณ ์๊ฐํ ์ ์์ง๋ง, ์ด๋ ๋ฆฌ์ค์ฝํ ์นํ ์์น์ ์๋ฐ๋๋ค. ๋ฆฌ์ค์ฝํ ์นํ ์์น์ ๋ฐ๋ฅด๋ฉด, ์ด๋ค ํ์
์ ์์ด ์ค์ํ ์์ฑ์ด๋ผ๋ฉด, ๊ทธ ํ์ ํ์
์์๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์ค์ํ๋ค. ๋ฐ๋ผ์ ๊ทธ ํ์
์ ๋ชจ๋ ๋ฉ์๋๊ฐ ํ์ ํ์
์์๋ ๋๊ฐ์ด ์ ๋์ํด์ผํ๋ค.
๊ตฌ์ฒด ํด๋์ค์ ํ์ ํด๋์ค์์ ๊ฐ์ ์ถ๊ฐํ ๋ฐฉ๋ฒ์ ์์ง๋ง ์ฐํํ ์ ์๋ ๋ฐฉ๋ฒ์ด ํ๋์๋ค. [์์๋์ ์ปดํฌ์ง์ ์ ์ฌ์ฉํด๋ผ - Item18]
public class ColorPoint {
private final Color color;
private final Point point;
public ColorPoint(int x, int y, Color color){
this.point = new Point(x,y);
this.color = Objects.requireNonNull(color);
}
public Point asPoint(){
return point;
}
@Override
public boolean equals(Object o){
if(!(o instanceof ColorPoint))
return false;
ColorPoint cp = (ColorPoint) o;
return cp.point.equals(point) && cp.color.equals(cp);
}
}
Point p = new Point(1,2);
ColorPoint cp = new ColorPoint(1,2, Color.RED);
ColorPoint cp2 = new ColorPoint(1,2,Color.BLACK);
System.out.println(cp.equals(p)); // false
System.out.println(p.equals(cp2)); // false
System.out.println(cp2.equals(cp)); // false
Point
๋ฅผ ์์ํ๋ ๋์ Point
๋ฅผ ColorPoint
์ private ํ๋๋ก ๋๊ณ , ColorPoint
์ ๊ฐ์ ์์น์ ์ผ๋ฐ Point
๋ฅผ ๋ฐํํ๋ ๋ทฐ๋ฅผ public์ผ๋ก ์ถ๊ฐํ๋ ๋ฐฉ์์ด๋ค. ์๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์๋ ๊ตฌ์ฒด ํด๋์ค๋ฅผ ํ์ฅํด ๊ฐ์ ์ถ๊ฐํ ํด๋์ค๊ฐ ์๋ค.
public class Timestamp extends java.util.Date {
/**
* @serial
*/
private int nanos;
...
}
Timestamp
๋ Date
๋ฅผ ์์๋ฐ์ ํ nanos ํ๋๋ฅผ ์ถ๊ฐํ๋ค. ๊ทธ ๊ฒฐ๊ณผ Timestamp
์ equals๋ ๋์นญ์ฑ์ ์๋ฐฐํด, Date ๊ฐ์ฒด์ ํ ์ปฌ๋ ์
์ ๋ฃ๊ฑฐ๋ ์๋ก ์์ด ์ฌ์ฉํ๋ฉด, ์๋ฑํ๊ฒ ๋์ํ ์ ์๋ค. Timestamp
์ API ์ค๋ช
์ ์ฃผ์์ฌํญ์ ์ธ๊ธํ๊ณ ์๋ค.
์ผ๊ด์ฑ
๋ ๊ฐ์ฒด๊ฐ ๊ฐ๋ค๋ฉด ์ด๋ ํ๋ ํน์ ๋ ๊ฐ์ฒด๊ฐ ์์ ๋์ง ์๋ ํ ์์ผ๋ก๋ ์์ํ ๊ฐ์์ผ ํ๋ค๋ ์๋ฏธ์ด๋ค. ๊ฐ๋ณ ๊ฐ์ฒด๋ ๋น๊ต ์์ ์ ๋ฐ๋ผ ์๋ก ๋ค๋ฅผ ์๋ ๊ฐ์ ์๋ ์๋ ๋ฐ๋ฉด, ๋ถ๋ณ ๊ฐ์ฒด๋ ํ๋ฒ ๋ค๋ฅด๋ฉด ๋๊น์ง ๋ฌ๋ผ์ผํ๋ค. ์ฆ, ๋ถ๋ณ ํด๋์ค๋ฅผ ๋ง๋ค๊ธฐ๋ก ํ๋ค๋ฉด eqauls๋ ํ๋ฒ ๊ฐ๋ค๊ณ ํ ๊ฐ์ฒด์ ์์ํ ๊ฐ๊ณ , ํ๋ฒ ๋ค๋ฅธ ๊ฐ์ฒด์๋ ์์ํ ๋ค๋ฅด๋๋ก ๋ง๋ค์ด์ผํ๋ค.
ํด๋์ค๊ฐ ๋ถ๋ณ์ด๋ ๊ฐ๋ณ์ด๋ equals์ ํ๋จ์ ์ ๋ขฐํ ์ ์๋ ์์์ด ๋ผ์ด๋ค๊ฒ ํด์๋ ์๋๋ค. (
java.net.URL
์ equlas๋ ์ผ๊ด์ฑ์ ์ด๊น)
NULL-์๋
๋ชจ๋ ๊ฐ์ฒด๊ฐ null
๊ณผ ๊ฐ์ง ์์์ผํ๋ค. ์ค์๋ก NullPointException
์ ๋์ง๋ ์ฝ๋๋ ํํ ๊ฒ์ด์ง๋ง, ์ด ๊ท์ฝ์ ์ด๋ฌํ ๊ฒฝ์ฐ๋ ํ์ฉํ์ง ์๋๋ค. ๋์น์ฑ ๊ฒ์ฌ๋ฅผ ํ๋ ค๋ฉด equals๋ ๊ฐ์ฒด๋ฅผ ์ ์ ํ ํ๋ณํํ ํ ํ์ ํ๋๋ค์ ๊ฐ์ ์์์ผํ๋๋ฐ, ์ด๋ ํ๋ณํ์ ์์ instanceof
์ฐ์ฐ์๋ก ์
๋ ฅ ๋งค๊ฐ๋ณ์๊ฐ ์ฌ๋ฐ๋ฅธ ํ์
์ธ์ง ๊ฒ์ฌํด์ผํ๋ค. instanceof
์ ์ฒซ๋ฒ์งธ ํผ์ฐ์ฐ์๊ฐ null์ด๋ฉด false๋ฅผ ๋ฐํํ๋ฏ๋ก ๋ฌต์์ ์ธ null๊ฒ์ฌ๋ฅผ ํ ์ ์๋ค.
@Override
public boolean equals(Object o){
if(!(o instanseof MyType))
return false;
MyType mt = (Mytype) o;
equals ๊ตฌํ ์ฃผ์ ์ฌํญ
==
์ฐ์ฐ์๋ฅผ ์ฌ์ฉํด ์ ๋ ฅ์ด ์๊ธฐ ์์ ์ ์ฐธ์กฐ์ธ์ง ํ์ธinstanceof
์ฐ์ฐ์๋ก ์ ๋ ฅ์ด ์ฌ๋ฐ๋ฅธ ํ์ ์ธ์ง ํ์ธ์ ๋ ฅ์ ์ฌ๋ฐ๋ฅธ ํ์ ์ผ๋ก ํ๋ณํ ํ๋ค.
์ ๋ ฅ ๊ฐ์ฒด์ ์๊ธฐ ์์ ์ ๋์๋๋ 'ํต์ฌ' ํ๋๋ค์ด ๋ชจ๋ ์ผ์นํ๋์ง ํ๋์ฉ ๊ฒ์ฌํ๋ค.
์ด๋, float
, double
์ ์ ์ธํ ๊ธฐ๋ณธ ํ์
ํ๋๋ ==
๋ก ๋น๊ตํ๊ณ , ์ฐธ์กฐ ํ์
ํ๋๋ ๊ฐ๊ฐ์ equals
๋ฉ์๋๋ก, float
, double
์ Float.compare(float, float)
์ Double.compare(double,double)
๋ก ๋น๊ตํ๋ค. Float.equals
์ Double.equals
๋ ์คํ ๋ฐ์ฑ์ ์๋ฐํ ์ ์์ด ์ฑ๋ฅ์ ์ข์ง ์๋ค.
๋๋ก๋ null๊ฐ๋ ์ ์ ๊ฐ์ผ๋ก ์ทจ๊ธํ๋ ์ฐธ์กฐ ํ์
ํ๋๋ ์๋๋ฐ, ์ด๋๋ Objects.equals(Object, Object)
๋ก ๋น๊ตํด NullPointerException
์ ์๋ฐฉํด์ผํ๋ค.
๋ํ, ์ด๋ค ํ๋๋ฅผ ๋จผ์ ๋น๊ตํ๋๋๊ฐ equals์ ์ฑ๋ฅ์ ์ข์ฐํ๊ธฐ๋ ํ๋๋ฐ, ์ต์์ ์ฑ๋ฅ์ ์ํด์๋ ๋ค๋ฅผ ๊ฐ๋ฅ์ฑ์ด ํฌ๊ฑฐ๋ ๋น๊ตํ๋ ๋น์ฉ์ด ์ผ ํ๋๋ฅผ ๋จผ์ ๋น๊ตํ๋ ๊ฒ์ด ์ข๋ค. ๋ ผ๋ฆฌ์ ์ํ์ ๊ด๋ จ ์๋ ํ๋๋ ๋น๊ตํด์๋ ์๋๋ฉฐ, ํต์ฌ ํ๋๋ก ๊ณ์ฐํ ์ ์๋ ํ์ ํ๋๋ ๊ตณ์ด ๋น๊ตํ ํ์๋ ์๋ค. ํ์ง๋ง, ํ์ ํ๋๊ฐ ๊ฐ์ฒด ์ ์ฒด์ ์ํ๋ฅผ ๋ํํ๋๊ฒฝ์ฐ ํ์ํ๋๋ฅผ ๋น๊ตํ๋ ๊ฒ์ด ๋ ๋น ๋ฅผ ์ ์๋ค.
equals๋ฅผ ์ฌ์ ์ํ ๋๋ hashCode๋ ๋ฐ๋์ ์ฌ์ ์ ํด์ผํ๋ค.(item 11)
Object
์ธ์ ํ์ ์ ๋งค๊ฒจ๋ณ์๋ก ๋ฐ๋ equals๋ ์ ์ธํ์ง ๋ง์.
AutoValue ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ฉด equals
์ hashCode
๋ฅผ ์์ฑํด์ค๋ค. (AutoValue ์์ ๋ณด๊ธฐ)
@AutoValue
<dependencies>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class Product {
public abstract String name();
public abstract String price();
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder name(String name);
public abstract Builder price(String price);
public abstract Product build();
}
public static Product.Builder builder() {
return new AutoValue_Product.Builder();
}
}
Last updated
Was this helpful?