Study/Effective Java

์•„์ดํ…œ10. equals๋Š” ์ผ๋ฐ˜ ๊ทœ์•ฝ์„ ์ง€์ผœ ์žฌ์ •์˜ํ•˜๋ผ

sw_develop 2023. 5. 6. 22:53

๐Ÿ“์ƒํ™ฉ

  • Object ํด๋ž˜์Šค์˜ equals() ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋ฌด์—‡์ผ๊นŒ?
  • ์˜ค๋ฒ„๋ผ์ด๋”ฉ์„ ํ•  ๋•Œ ์ง€์ผœ์•ผ ํ•  ์ผ๋ฐ˜ ๊ทœ์•ฝ์€ ๋ฌด์—‡์ผ๊นŒ?

 

๐Ÿ“๋ฐฉ๋ฒ•

โ–ถ๏ธ equals๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒฝ์šฐ

  • ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ์œผ๋กœ ๊ทธ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋Š” ์˜ค์ง ์ž๊ธฐ ์ž์‹ ๊ณผ๋งŒ ๊ฐ™๊ฒŒ ๋จ
  • ๋‹ค์Œ์—์„œ ์—ด๊ฑฐํ•œ ์ƒํ™ฉ ์ค‘ ํ•˜๋‚˜์— ํ•ด๋‹นํ•œ๋‹ค๋ฉด ์žฌ์ •์˜ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ตœ์„ ์ž„

1. ๊ฐ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋ณธ์งˆ์ ์œผ๋กœ ๊ณ ์œ ํ•จ

  • ๊ฐ’์„ ํ‘œํ˜„ํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ๋™์ž‘ํ•˜๋Š” ๊ฐœ์ฒด๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ ์—ฌ๊ธฐ ํ•ด๋‹นํ•จ
  • ex) Thread --> Object์˜ equals ๋ฉ”์„œ๋“œ๋Š” ์ด๋Ÿฌํ•œ ํด๋ž˜์Šค์— ๋”ฑ ๋งž๊ฒŒ ๊ตฌํ˜„๋˜์—ˆ์Œ

2. ์ธ์Šคํ„ด์Šค์˜ '๋…ผ๋ฆฌ์  ๋™์น˜์„ฑ(logical equality)'์„ ๊ฒ€์‚ฌํ•  ์ผ์ด ์—†์Œ

  • ์ธ์Šคํ„ด์Šค์˜ ๋‚ด๋ถ€ ๊ฐ’์ด ๋™์ผํ•œ์ง€ ๊ฒ€์‚ฌํ•˜์ง€ ์•Š์•„๋„ ๋จ

3. ์ƒ์œ„ ํด๋ž˜์Šค์—์„œ ์žฌ์ •์˜ํ•œ equals๊ฐ€ ํ•˜์œ„ ํด๋ž˜์Šค์—๋„ ๋“ค์–ด๋งž์Œ

  • Set ๊ตฌํ˜„์ฒด๋Š” AbstractSet์ด ๊ตฌํ˜„ํ•œ equals๋ฅผ ์ƒ์†๋ฐ›์•„ ์“ฐ๊ณ , List๋‚˜ Map ๊ตฌํ˜„์ฒด๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž„

4. ํด๋ž˜์Šค๊ฐ€ private์ด๊ฑฐ๋‚˜ package-private์ด๊ณ  equals ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ผ์ด ์—†์Œ

 

โ–ถ๏ธ equals๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•ด์•ผ๋˜๋Š” ๊ฒฝ์šฐ

์ธ์Šคํ„ด์Šค์˜ '๋…ผ๋ฆฌ์  ๋™์น˜์„ฑ'์„ ํ™•์ธํ•ด์•ผ ํ•  ๋•Œ

  • ๊ฐ์ฒด ์‹๋ณ„์„ฑ(object identity : ๋‘ ๊ฐ์ฒด๊ฐ€ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๊ฐ™์€๊ฐ€)์ด ์•„๋‹ˆ๋ผ ๋…ผ๋ฆฌ์  ๋™์น˜์„ฑ์„ ํ™•์ธํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ƒ์œ„ ํด๋ž˜์Šค์˜ equals๊ฐ€ ๋…ผ๋ฆฌ์  ๋™์น˜์„ฑ์„ ๋น„๊ตํ•˜๋„๋ก ์žฌ์ •์˜๋˜์ง€ ์•Š์•˜์„ ๋•Œ๋ฅผ ์˜๋ฏธํ•จ
  • ์ฃผ๋กœ ๊ฐ’ ํด๋ž˜์Šค๋“ค์ด ์—ฌ๊ธฐ์— ํ•ด๋‹นํ•จ
    • ๊ฐ’ ํด๋ž˜์Šค๋ž€?
      • Integer์™€ String์ฒ˜๋Ÿผ ๊ฐ’์„ ํ‘œํ˜„ํ•˜๋Š” ํด๋ž˜์Šค
    • ๋‘ ๊ฐ’ ๊ฐ์ฒด๋ฅผ equals๋กœ ๋น„๊ตํ•˜๋Š” ๊ฒฝ์šฐ ๊ฐ์ฒด๊ฐ€ ๊ฐ™์€์ง€๊ฐ€ ์•„๋‹ˆ๋ผ ๊ฐ’์ด ๊ฐ™์€์ง€๋ฅผ ์•Œ๊ณ  ์‹ถ์Œ
    • equals๊ฐ€ ๋…ผ๋ฆฌ์  ๋™์น˜์„ฑ์„ ํ™•์ธํ•˜๋„๋ก ์žฌ์ •์˜ํ•˜์—ฌ ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , Map์˜ ํ‚ค์™€ Set์˜ ์›์†Œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋จ (์ค‘๋ณต๋˜์ง€ ์•Š์•„์•ผ ํ•˜๋Š” ๊ฒƒ๋“ค)
    • ์˜ˆ์™ธ๋Š”?
      • ๊ฐ’ ํด๋ž˜์Šค์—ฌ๋„, ๊ฐ’์ด ๊ฐ™์€ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋‘˜ ์ด์ƒ ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š์Œ์„ ๋ณด์žฅํ•˜๋Š” ์ธ์Šคํ„ด์Šค ํ†ต์ œ ํด๋ž˜์Šค๋ผ๋ฉด equals๋ฅผ ์žฌ์ •์˜ํ•˜์ง€ ์•Š์•„๋„ ๋จ (Enum ํด๋ž˜์Šค๋„ ์—ฌ๊ธฐ์— ํ•ด๋‹น)
      • ์–ด์ฐจํ”ผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค๊ฐ€ 2๊ฐœ ์ด์ƒ ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š์œผ๋ฏ€๋กœ ๋…ผ๋ฆฌ์  ๋™์น˜์„ฑ๊ณผ ๊ฐ์ฒด ์‹๋ณ„์„ฑ์ด ์‚ฌ์‹ค์ƒ ๋˜‘๊ฐ™์€ ์˜๋ฏธ๊ฐ€ ๋จ

 

โ–ถ๏ธ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ์‹œ ๋ฐ˜๋“œ์‹œ ๋”ฐ๋ผ์•ผ ํ•˜๋Š” ์ผ๋ฐ˜ ๊ทœ์•ฝ

  • ๊ทœ์•ฝ์„ ๋”ฐ๋ผ์•ผ ํ•˜๋Š” ์ด์œ ?
    • ํ•œ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋Š” ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ๋นˆ๋ฒˆํžˆ ์ „๋‹ฌ๋˜์–ด ์‚ฌ์šฉ๋จ
    • ์ด๋•Œ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ชฝ์—์„œ๋Š” ์ „๋‹ฌ๋ฐ›์€ ๊ฐ์ฒด๊ฐ€ equals ๊ทœ์•ฝ์„ ์ง€ํ‚จ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ๋™์ž‘ํ•จ
  • ๋‹ค์Œ์€ Object ๋ช…์„ธ์— ์ ํžŒ ๊ทœ์•ฝ์ž„

1. ๋ฐ˜์‚ฌ์„ฑ

  • ๊ฐ์ฒด๋Š” ์ž๊ธฐ ์ž์‹ ๊ณผ ๊ฐ™์•„์•ผ ํ•จ

2.  ๋Œ€์นญ์„ฑ

  • ๋‘ ๊ฐ์ฒด๋Š” ์„œ๋กœ์— ๋Œ€ํ•œ ๋™์น˜ ์—ฌ๋ถ€์— ๋˜‘๊ฐ™์ด ๋‹ตํ•ด์•ผ ํ•จ

3. ์ถ”์ด์„ฑ

  • ๊ทธ ์ž์ฒด๋กœ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ์ด ๊ฐ€๋Šฅํ•œ ๊ตฌ์ฒด ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•ด ์ƒˆ๋กœ์šด ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜๋ฉด์„œ equals ๊ทœ์•ฝ์„ ๋งŒ์กฑ์‹œํ‚ฌ ๋ฐฉ๋ฒ•์€ ์กด์žฌํ•˜์ง€ ์•Š์Œ
    • ์ถ”์ƒ ํด๋ž˜์Šค์˜ ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ๋ผ๋ฉด equals ๊ทœ์•ฝ์„ ์ง€ํ‚ค๋ฉด์„œ ๊ฐ’ ์ถ”๊ฐ€๊ฐ€ ๊ฐ€๋Šฅํ•จ (์ƒ์œ„ ํด๋ž˜์Šค๋ฅผ ์ง์ ‘ ์ธ์Šคํ„ด์Šค๋กœ ๋งŒ๋“œ๋Š” ๊ฒŒ ๋ถˆ๊ฐ€๋Šฅ ํ•˜๋‹ˆ๊นŒ)
  • ์šฐํšŒ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” '์ƒ์† ๋Œ€์‹  ์ปดํฌ์ง€์…˜ ์‚ฌ์šฉ' ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Œ
    • ex) Point๋ฅผ ์ƒ์†ํ•˜๋Š” ๋Œ€์‹  Point๋ฅผ ColorPoint์˜ private ํ•„๋“œ๋กœ ๋‘ 

 

์ฝ”๋“œ 10-5 equals ๊ทœ์•ฝ์„ ์ง€ํ‚ค๋ฉด์„œ ๊ฐ’ ์ถ”๊ฐ€ํ•˜๊ธฐ

public class ColorPoint {
    private final Point point;
    private final Color color;
    
    public ColorPoint(int x, int y, Color color) {
    	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 cp.point.equals(this.point) && cp.color.equals(this.color);
    }
    
    ...
}

4. ์ผ๊ด€์„ฑ

  • ๋‘ ๊ฐ์ฒด๊ฐ€ ๊ฐ™๋‹ค๋ฉด ์•ž์œผ๋กœ๋„ ์˜์›ํžˆ ๊ฐ™์•„์•ผ ํ•จ์„ ์˜๋ฏธํ•จ (๋ถˆ๋ณ€ ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ)

5. null-์•„๋‹˜

  • ๋ชจ๋“  ๊ฐ์ฒด๊ฐ€ null๊ณผ ๊ฐ™์ง€ ์•Š์•„์•ผ ํ•จ
//๋ช…์‹œ์  null ๊ฒ€์‚ฌ - ํ•„์š” ์—†๋‹ค!
@Override
public boolean equals(Object o) {
    if (o == null) return false;
    ...
}

//๋ฌต์‹œ์  null ๊ฒ€์‚ฌ - ๊ถŒ์žฅ!
@Override
public boolean equals(Object o) {
    if (!(o instanceof MyType)) return false;
    MyType mt = (MyType) o;
    ...
}

 

โ–ถ๏ธ equals ๋ฉ”์„œ๋“œ ๊ตฌํ˜„ ๋ฐฉ๋ฒ• ๋‹จ๊ณ„๋ณ„ ์ •๋ฆฌ

1. == ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•ด ์ž…๋ ฅ์ด ์ž๊ธฐ ์ž์‹ ์˜ ์ฐธ์กฐ์ธ์ง€ ํ™•์ธํ•จ

  • ์ž๊ธฐ ์ž์‹ ์ด๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•จ
  • ์ด๋Š” ๋‹จ์ˆœํ•œ ์„ฑ๋Šฅ ์ตœ์ ํ™”์šฉ์œผ๋กœ, ๋น„๊ต ์ž‘์—…์ด ๋ณต์žกํ•œ ์ƒํ™ฉ์ผ ๋•Œ ์œ ์šฉํ•จ

2. instanceof ์—ฐ์‚ฐ์ž๋กœ ์ž…๋ ฅ์ด ์˜ฌ๋ฐ”๋ฅธ ํƒ€์ž…์ธ์ง€ ํ™•์ธํ•จ

3. ์ž…๋ ฅ์„ ์˜ฌ๋ฐ”๋ฅธ ํƒ€์ž…์œผ๋กœ ํ˜•๋ณ€ํ™˜ํ•จ

  • 2์—์„œ instanceof ๊ฒ€์‚ฌ๋ฅผ ํ–ˆ์œผ๋ฏ€๋กœ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ

4. ์ž…๋ ฅ ๊ฐ์ฒด์™€ ์ž๊ธฐ ์ž์‹ ์˜ ๋Œ€์‘๋˜๋Š” 'ํ•ต์‹ฌ' ํ•„๋“œ๋“ค์ด ๋ชจ๋‘ ์ผ์น˜ํ•˜๋Š”์ง€ ํ•˜๋‚˜์”ฉ ๊ฒ€์‚ฌํ•จ

  • ๋ชจ๋“  ํ•„๋“œ๊ฐ€ ์ผ์น˜ํ•˜๋ฉด true, ํ•˜๋‚˜๋ผ๋„ ๋‹ค๋ฅด๋‹ค๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•จ

 

โ–ถ๏ธ ํ•„๋“œ ๋น„๊ต ๋ฐฉ์‹

  • float์™€ double์„ ์ œ์™ธํ•œ ๊ธฐ๋ณธ ํƒ€์ž… ํ•„๋“œ๋Š” == ์—ฐ์‚ฐ์ž๋กœ ๋น„๊ต
  • ์ฐธ์กฐ ํƒ€์ž… ํ•„๋“œ๋Š” equals ๋ฉ”์„œ๋“œ๋กœ ๋น„๊ต
  • float์™€ double์€ ๊ฐ๊ฐ ์ •์  ๋ฉ”์„œ๋“œ์ธ Float.compare(float, float)์™€ Double.compare(double, double)๋กœ ๋น„๊ต
    • Float.NaN, -0.0f, ํŠน์ˆ˜ํ•œ ๋ถ€๋™์†Œ์ˆ˜ ๊ฐ’ ๋“ฑ์„ ๋‹ค๋ค„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž„
  • null๋„ ์ •์ƒ ๊ฐ’์œผ๋กœ ์ทจ๊ธ‰ํ•˜๋Š” ์ฐธ์กฐ ํƒ€์ž… ํ•„๋“œ์˜ ๊ฒฝ์šฐ, ์ •์  ๋ฉ”์„œ๋“œ์ธ Objects.equals(Object, Object)๋กœ ๋น„๊ต
    • NullPointerException ๋ฐฉ์ง€๋ฅผ ์œ„ํ•จ
  • ๋น„๊ตํ•˜๊ธฐ ๋ณต์žกํ•œ ํ•„๋“œ๋ฅผ ๊ฐ€์ง„ ํด๋ž˜์Šค์ธ ๊ฒฝ์šฐ, ๊ทธ ํ•„๋“œ์˜ ํ‘œ์ค€ํ˜•(canonical form)์„ ์ €์žฅํ•ด๋‘” ํ›„ ํ‘œ์ค€ํ˜•๋ผ๋ฆฌ ๋น„๊ต
    • ํŠนํžˆ ๋ถˆ๋ณ€ ํด๋ž˜์Šค์— ์ œ๊ฒฉ์ž„
    • ๊ฐ€๋ณ€ ๊ฐ์ฒด๋ผ๋ฉด ๊ฐ’์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ํ‘œ์ค€ํ˜•์„ ์ตœ์‹  ์ƒํƒœ๋กœ ๊ฐฑ์‹ ํ•ด์ค˜์•ผ ํ•จ

 

*์–ด๋–ค ํ•„๋“œ๋ฅผ ๋จผ์ € ๋น„๊ตํ•˜๋А๋ƒ๊ฐ€ equals์˜ ์„ฑ๋Šฅ์„ ์ขŒ์šฐํ•˜๊ธฐ๋„ ํ•จ!

  • ์ตœ์ƒ์˜ ์„ฑ๋Šฅ์„ ๋ฐ”๋ž€๋‹ค๋ฉด, ๋‹ค๋ฅผ ๊ฐ€๋Šฅ์„ฑ์ด ๋” ํฌ๊ฑฐ๋‚˜ ๋น„๊ตํ•˜๋Š” ๋น„์šฉ์ด ์‹ผ ํ•„๋“œ๋ฅผ ๋จผ์ € ๋น„๊ตํ•˜์ž

 

์ฝ”๋“œ 10-6 ์ „ํ˜•์ ์ธ equals ๋ฉ”์„œ๋“œ์˜ ์˜ˆ

public final class PhoneNumber {
    private final short areaCode, prefix, lineNum;
    
    ...
    
    @Override
    public boolean equals(Object o) {
    	if (o == this) return true;
        if (!(o instanceof PhoneNumber)) return false;
        PhoneNumber pn = (PhoneNumber) o;
        return pn.liNum == this.lineNum && pn.prefix == this.prefix && pn.areaCode == this.areaCode;
    }
    
    ...
}

 

โ–ถ๏ธ ์ฃผ์˜์‚ฌํ•ญ

1. equals๋ฅผ ์žฌ์ •์˜ํ•  ๋• hashCode๋„ ๋ฐ˜๋“œ์‹œ ์žฌ์ •์˜ํ•˜์ž

2. ๋„ˆ๋ฌด ๋ณต์žกํ•˜๊ฒŒ ํ•ด๊ฒฐํ•˜๋ ค ๋“ค์ง€ ๋ง์ž

  • ํ•„๋“œ๋“ค์˜ ๋™์น˜์„ฑ๋งŒ ๊ฒ€์‚ฌํ•ด๋„ equals ๊ทœ์•ฝ์„ ์–ด๋ ต์ง€ ์•Š๊ฒŒ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ์Œ

3. Object ์™ธ์˜ ํƒ€์ž…์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๋Š” equals ๋ฉ”์„œ๋“œ๋Š” ์„ ์–ธํ•˜์ง€ ๋ง์ž

//์ž˜๋ชป๋œ ์˜ˆ
public boolean equals(MyClass o) {
	...
}

 

๐Ÿ“ํ•ต์‹ฌ ์ •๋ฆฌ

  • ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด equals๋ฅผ ์žฌ์ •์˜ํ•˜์ง€ ๋ง์ž
    • ๋งŽ์€ ๊ฒฝ์šฐ์— Object์˜ equals๊ฐ€ ์›ํ•˜๋Š” ๋น„๊ต๋ฅผ ์ •ํ™•ํžˆ ์ˆ˜ํ–‰ํ•ด์คŒ
  • ์žฌ์ •์˜ํ•  ๋•Œ๋Š” ๊ทธ ํด๋ž˜์Šค์˜ ํ•ต์‹ฌ ํ•„๋“œ๋ฅผ ๋ชจ๋‘, ๋‹ค์„ฏ ๊ฐ€์ง€ ๊ทœ์•ฝ์„ ํ™•์‹คํžˆ ์ง€์ผœ๊ฐ€๋ฉฐ ๋น„๊ตํ•ด์•ผ ํ•จ