item 10 huisoo - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki
- equals ๋ฉ์๋๋ Object์ ์ ๋ณด๋ค์ ๋ํด ๋๋ฑ์ฑ์ ๋น๊ตํ๋ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉํ๋ค.
- equals ๋ฉ์๋๋ฅผ ์๋ชป ์์ฑํ๋ฉด ์๋ชป๋ ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค ์ ์๋ค.
- ๊ฐ ์ธ์คํด์ค๊ฐ ๋ณธ์ง์ ์ผ๋ก ๊ณ ์ ํ๋ค
๊ฐ์ด ์๋ ๋์์ ๊ฐ์ฒด๋ก ํํํ๋ ๊ฒฝ์ฐ (ex. Thread)
- ๋ ผ๋ฆฌ์ ๋์น์ฑ(p->q, ~q->~p)์ ํ์ธ ํ ํ์๊ฐ ์๋ค
ํด๋ผ์ด์ธํธ๊ฐ ์์น ์๊ฑฐ๋ ์ ์ด์ ํ์์น ์๋ค๊ณ ํ๋จ ํ ์ ์๋ค(๊ธฐ๋ณธ equals๋ก ํด๊ฒฐ).
- ์์ํด๋์ค์์ ์ฌ์ ์ํ equals๊ฐ ํ์ ํด๋์ค์๋ ์ ์ฉ๋๋ค.
set, Map, List์ ๊ฒฝ์ฐ Abstract(Type)์ equals๋ฅผ ์ด๋ค.
- ํด๋์ค๊ฐ private, package-private์ฌ์ equals๋ฅผ ํธ์ถํ ์ผ์ด ์๋ ๊ฒฝ์ฐ
- ์ฑ๊ธํด์ ๋ณด์ฅํ๋ ํด๋์ค(์ธ์คํด์ค ํต์ ํด๋์ค, Enum(์ด๊ฑฐํ์ ))์ธ ๊ฒฝ์ฐ- ๊ฐ์ฒด ๊ฐ ๋๋ฑ์ฑ, ๋์ผ์ฑ์ด ๋ณด์ฅ๋๋ค.
null์ด ์๋ ๋ชจ๋ ์ฐธ์กฐ ๊ฐ x์ ๋ํด x.equals(x)๋ฅผ ๋ง์กฑํด์ผํ๋ค. ์๊ธฐ ์์๊ณผ ๋น๊ตํ์ ๋ ๊ฐ์์ผ ํ๋ค.
null์ด ์๋ ๋ชจ๋ ์ฐธ์กฐ ๊ฐ x,y์ ๋ํด x.equals(y)๊ฐ true ์ด๋ฉด y.equals(x) true๋ฅผ ๋ง์กฑํด์ผํ๋ค.
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;
}
}
- ์ ์ฝ๋ ์คํ์ x.equals(y)๋ true์ด์ง๋ง y.equals(x)๋ false๋ก ๋์นญ์ฑ์ ์๋ฐํ๋ค.
- String ํด๋์ค์์๋ CaseInsentiveString ํด๋์ค๋ฅผ ๋ชจ๋ฅธ๋ค.
@Override
public boolean equals(Object o) {
return o instanceof CaseInsensitiveString && ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}
//ํ๋ณํ์ ํตํด ๊ฐ๋จํ ์ฒ๋ฆฌ ๊ฐ๋ฅ
null์ด ์๋ ๋ชจ๋ ์ฐธ์กฐ ๊ฐ x,y,z์ ๋ํด x.equals(y)๊ฐ true์ด๊ณ , y.equals(z)๊ฐ true์ด๋ฉด x.equals(z)๋ true๋ฅผ ๋ง์กฑํด์ผ ํ๋ค.
์์) Pointํด๋์ค์ ColorPointํด๋์ค(ColorPoint๋ Pointํด๋์ค๋ฅผ ์์ํ ํด๋์ค์ด๋ค.)
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 this.x == p.x && this.y == p.y;
}
}
//equals ๋ฉ์๋๋ฅผ ๊ทธ๋๋ก ๋๋ฉด ์์ ์ ๋ณด๋ ๋ฌด์ํ ์ฑ ๋น๊ต๋ฅผ ํ๋ค.
//color ์ ๋ณด๋ฅผ ๋์น๋ ๋ฐ์ ๋ค์ผ ์ ์๋ค.
class ColorPoint extends Point {
private final Color color;
@Override
public ColorPoint(int x, int y, Color color) {
super(x,y);
this.color = color;
}
}
- ๋์นญ์ฑ ์๋ฐฐ ์ฝ๋
class ColorPoint extends Point {
private final Color color;
@Override
public boolean equals(Object o) {
if(!(o instanceof ColorPoint)) return false;
return super.equals(o) && this.color == ((ColorPoint) o).color;
}
}
Point p = new Point(1,2);
ColorPoint cp = new ColorPoint(1, 2, Color.RED);
// Point๋ฅผ ColorPoint์ ๋น๊ตํ ๊ฒฐ๊ณผ์ ColorPoint๋ฅผ Point์ ๋น๊ตํ ๊ฒฐ๊ณผ๊ฐ ๋ค๋ฅผ ์ ์๋ค.
System.out.println(p.equals(cp)); //true --> ์์์ ๋ฌด์ํจ
System.out.println(cp.equals(p)); //false -->ํด๋์ค๊ฐ ๋ค๋ฅด๋ค๋ฉฐ false
- ์ถ์ด์ฑ ์๋ฐ Case
class ColorPoint extends Point {
private final Color color;
@Override
public boolean equals(Object o) {
if(!(o instanceof Point)) return false;
//o๊ฐ ์ผ๋ฐ Point์ด๋ฉด ์์์ ๋ฌด์ํ๊ณ x,y์ ๋ณด๋ง ๋น๊ตํ๋ค.
if(!(o instanceof ColorPoint)) return o.equals(this);
//o๊ฐ ColorPoint์ด๋ฉด ์์๊น์ง ๋น๊ตํ๋ค.
return super.equals(o) && this.color == ((ColorPoint) o).color;
}
}
ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
Point p2 = new Point(1, 2);
ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);
System.out.println(p1.equals(p2)); //true --> ์์ ๋ฌด์
System.out.println(p2.equals(p3)); //true --> ์์ ๋ฌด์
System.out.println(p1.equals(cp3)); //false --> ์ถ์ด์ฑ์ด ๊นจ์ก๋ค. ์์๊น์ง ๊ณ ๋ ค(Red != BLUE)
- ๋ฌดํ์ฌ๊ท ๋ฐ์ case
class SmellPoint extends Point {
private final Smell smell;
@Override
public boolean equals(Object o) {
if(!(o instanceof Point)) return false;
//o๊ฐ ์ผ๋ฐ Point์ด๋ฉด ์์์ ๋ฌด์ํ๊ณ x,y์ ๋ณด๋ง ๋น๊ตํ๋ค.
if(!(o instanceof SmellPoint)) return o.equals(this);
//o๊ฐ ColorPoint์ด๋ฉด ์์๊น์ง ๋น๊ตํ๋ค.
return super.equals(o) && this.smell == ((SmellPoint) o).smell;
}
}
Point p1 = new ColorPoint(1, 2, Color.RED);
Point p2 = new SmellPoint(1, 2, Smell.SWEET);
System.out.println(p1.equals(p2)); // StackOverflow
//StackOverflow ์ด์
/*p1๋ ColorPoint ํด๋์ค์ ์ธ์คํด์ค์ด๋ค.
p1.equals(p2) ์ฝ๋๋ ColorPoint์ equals ๋ฉ์๋๋ฅผ ํ๋ค.
ColorPoint์ 2๋ฒ์งธ if์ ๊ฑธ๋ฆฐ๋ค(o๋ SmellPoint ํ์
์ด๊ธฐ ๋๋ฌธ์, SmellPoint๋ Point์ด์ง๋ง ColorPoint๋ ์๋)
o๊ฐ SmellPoint์ด๋ฏ๋ก SmellPoint์ equals๋ฅผ ํ๋ค.
๋ค์ ๋ ๋ฒ์งธ if์์ ๊ฑธ๋ฆฐ๋ค.
๋ ๋ค์ ColorPoint์ equals๋ฅผ ํ๋ค.
๋ฌดํ ์ฌ๊ท๊ฐ ๋ฐ์ํ๋ค.*/
๊ฐ์ฒด์์ฑ๊ฐ๋ฅ(instantiable)๋ฅผ ์์ํ์ฌ ์๋ก์ด ์์ฑ์ ์ถ๊ฐํ๋ฉด equals ๊ท์ฝ์ ์ด๊ธฐ์ง ์์ ๋ฐฉ๋ฒ์ด ์๋ค. ์์์ ์ด์ฉํ ๊ฒฝ์ฐ ๋์น๊ด๊ณ๊ฐ ๊นจ์ง๋ค. (getClass๋ฅผ ์ด์ฉํ๋ ๊ฒฝ์ฐ๋ ๋ฌธ์ ๊ฐ ์๊ธด๋ค.)
- ๋ฆฌ์ค์ฝํ ์นํ ์์น
์ด๋ค ํ์ ์ ์์ด ์ค์ํ๋ค๋ฉด, ๊ทธ ํ์ ํ์ ์์๋ ์ค์ํ๋ค. ์์ ํด๋์ค๋ ๋ถ๋ชจ ํด๋์ค์์ ๊ฐ๋ฅํ ํ์๋ฅผ ์ํํ ์ ์์ด์ผ ํ๋ค. ๋ถ๋ชจ.equals(์์) = false
class Point {
private final int x;
private final int y;
private static final Set<Point> unitCircle = Set.of(new Point(0, -1),
new Point(0, 1),
new Point(-1, 0),
new Point(1, 0)
);
public static boolean onUnitCircle(Point p) {
return unitCircle.contains(p);
}
@Override
public boolean equals(Object o) {
if(o == null || o.getClass() != this.getClass()) {
return false;
}
Point p = (Point) o;
return this.x == p.x && this.y = p.y;
}
}
//๊ฐ์ ๊ตฌํ ํด๋์ค์ ๊ฐ์ฒด์ ๋น๊ตํ ๋๋ง true๋ฅผ ๋ฐํํ๋ค.
- ์์๋์ ์ปดํฌ์ง์ ์ ์ฌ์ฉํ๋ผ ์์ ๋์ ์ Point ๋ณ์๋ฅผ ๊ฐ๋๋ก ๊ตฌ์ฑํ๋ค.
public ColorPoint {
private Point point;
private Color color;
public ColorPoint(int x, int y, Color color) {
**** this.point = new Point(x, y);
this.color = Objects.requireNonNull(color);
}
****public Point asPoint() {
return this.point;
}
@Override
public boolean equals(Object o) {
if(!(o instanceof ColorPoint)) {
return false;
}
ColorPoint cp = (ColorPoint) o;
return this.point.equals(cp) && this.color.equals(cp.color);
}
}
null์ด ์๋ ๋ชจ๋ ์ฐธ์กฐ๊ฐ x,y์ ๋ํด x.equals(y)๋ฅผ ๋ฐ๋ณตํด์ ํธ์ถํ๋ฉด ํญ์ true๋ฅผ ๋ฐํํ๊ฑฐ๋ ํญ์ false๋ฅผ ๋ฐํํ๋ค.(๋ถ๋ณ๊ฐ์ฒด ์ฌ๋ถ)
null์ด ์๋ ๋ชจ๋ ์ฐธ์กฐ๊ฐ x์ ๋ํด x.equals(null)์ false์ด๋ค.
- == ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํด ์๊ธฐ ์์ ์ ์ฐธ์กฐ์ธ์ง ํ์ธํ๋ผ
์๊ธฐ์์ ์ด๋ฉด true
- instanceof ์ฐ์ฐ์๋ก ์ ๋ ฅ์ด ์ฌ๋ฐ๋ฅธ ํ์ ์ธ์ง ํ์ธํ๋ผ
null ๊ฒ์ฌ
- ์ ๋ ฅ์ ์ฌ๋ฐ๋ฅธ ํ์ ์ผ๋ก ํ๋ณํํ๋ผ.
- ์ ๋ ฅ ๊ฐ์ฒด์ ์๊ธฐ ์์ ์ ๋์๋๋ ํต์ฌ ํ๋๋ค์ด ๋ชจ๋ ์ผ์นํ๋์ง ํ๋์ฉ ๊ฒ์ฌํ๋ผ.
๊ธฐ๋ณธ ํ์ ํ๋๋ ==์ฐ์ฐ์๋ก ๋น๊ต ์ฐธ์กฐํ์ ํ๋๋ equals()๋ก ๋น๊ต float์ Float.compare(float,float), double์ Double.compare(double,double)๋ก ๋น๊ต, Float.NaN, -0.0f ๋๋ฌธ
- ์ฑ๋ฅ์ ์ํด ๋ค๋ฅผ ๊ฐ๋ฅ์ฑ์ด ๋ ํฌ๊ฑฐ๋, ๋น์ฉ์ด ์ผ ํ๋๋ฅผ ๋จผ์ ๋น๊ตํ๋ผ.
** ์ ๊ตฌํ๋ฐฉ๋ฒ์ผ๋ก ๋ค ๊ตฌํํ๋ค๋ฉด, ๋์นญ์ฑ/์ถ์ด์ฑ/์ผ๊ด์ฑ์ ํ์ธํ๋ค. ** ๋จ์ํ ์คํธ๋ฅผ ์์ฑํด ๋๋ ค๋ณด๊ฑฐ๋ AutoValue ์ ๋ ธํ ์ด์ ์ ์ฌ์ฉํ์. ** ์ฌ๋ < IDE < AutoValue
- equals๋ฅผ ์ฌ์ ์ํ ๋ hashCode๋ ๋ฐ๋์ ์ฌ์ ์ํ์(Item 11)
- ๋๋ฌด ๋ณต์กํ๊ฒ ํด๊ฒฐํ๋ ค ํ์ง ๋ง์.
- ํ๋์ ๋์น์ฑ๋ง ๊ฒ์ฌํด๋ equals ๊ท์ฝ์ ์ด๋ ต์ง ์๊ฒ ์งํฌ ์ ์๋ค. ๋ํ ์ผ๋ฐ์ ์ผ๋ก ๋ณ์นญ(alias)๋ ๋น๊ตํ์ง ์๋๊ฒ ์ข๋ค.
- object ์ธ์ ํ์ ์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ๋ equals ๋ฉ์๋๋ ์ ์ธํ์ง ๋ง์.
//์๋ชป๋ ์ - ์
๋ ฅ ํ์
์ ๋ฐ๋์ Object ์ฌ์ผ ํ๋ค!
//์ปดํ์ผ ๋์ง ์์, Object.equals๊ฐ ์๋์ผ๋ก ๋ค์ค์ ์ ํ ๊ฒ์ด๋ค.
@Override public boolean equals(MyClass o){
....
}
๊ผญ ํ์ํ ๊ฒฝ์ฐ๊ฐ ์๋๋ฉด equals๋ฅผ ์ฌ์ ์ํ์ง ๋ง์. ๋ง์ ๊ฒฝ์ฐ Object์ equals๊ฐ ๋น๊ต๋ฅผ ์ ํํ ์ํํ๋ค. ์ฌ์ ์ ํ ๋๋ ๊ทธ ํด๋์ค์ ํต์ฌํ๋ ๋ชจ๋๋ฅผ ๋น ์ง์์ด, ๋ค์ฏ๊ฐ์ง ๊ท์ฝ์ ํ์คํ ์ง์ผ๊ฐ๋ฉฐ ๋น๊ตํ๋ค.