Item 10. equals

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

equals๋Š” ์žฌ์ •์˜ํ•˜๊ธฐ ์‰ฌ์›Œ ๋ณด์ด์ง€๋งŒ, ์ž˜๋ชป ์žฌ์ •์˜ํ•˜๋ฉด ํ”„๋กœ๊ทธ๋žจ์ด ์˜ค๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋•Œ๋ฌธ์— ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ฉด ์žฌ์ •์˜ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹๊ณ , ๋‹ค์Œ์˜ ์ƒํ™ฉ์ด๋ฉด ์žฌ์ •์˜ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

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

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

  • ์ƒ์œ„ ํด๋ž˜์Šค์—์„œ ์žฌ์ •์˜ํ•œ equals๊ฐ€ ํ•˜์œ„ ํด๋ž˜์Šค์˜ equals์—์„œ๋„ ์•Œ๋งž๊ฒŒ ๋™์ž‘ํ•œ๋‹ค.

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

์œ„ ์ƒํ™ฉ์ด ์•„๋‹ˆ๋ผ ๋…ผ๋ฆฌ์  ๋™์น˜์„ฑ์„ ๊ฒ€์‚ฌํ•ด์•ผ ํ•œ๋‹ค๋ฉด, ๋‹ค์Œ์˜ ๊ทœ์•ฝ์„ ๋”ฐ๋ผ ์žฌ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.

equals ์žฌ์ •์˜ ๊ทœ์•ฝ

  • ๋ฐ˜์‚ฌ์„ฑ(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.equals(z)๋„ true๋‹ค.

  • ์ผ๊ด€์„ฑ(consistency): null์ด ์•„๋‹Œ ๋ชจ๋“  ์ฐธ์กฐ ๊ฐ’ x, y์— ๋Œ€ํ•ด, x.equals(y)๋ฅผ ๋ฐ˜๋ณตํ•ด์„œ ํ˜ธ์ถœํ•˜๋ฉด ํ•ญ์ƒ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ ํ•ญ์ƒ false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • null-์•„๋‹˜: null์ด ์•„๋‹Œ ๋ชจ๋“  ์ฐธ์กฐ ๊ฐ’ x์— ๋Œ€ํ•ด, x.equals(null)์€ false๋‹ค.

์–ผํ•๋ณด๋ฉด ๋‹น์—ฐํ•œ ๊ทœ์•ฝ๋“ค์ด์ง€๋งŒ, ์‹ค์ˆ˜๋กœ ์–ด๊ธธ ์ˆ˜ ์žˆ๋Š” ๊ทœ์•ฝ๋“ค์ด๋‹ค. ์œ„์˜ ๊ทœ์•ฝ๋“ค์„ ์–ด๊ธด ์˜ˆ์‹œ๋“ค์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

๋ฐ˜์‚ฌ์„ฑ

์•…์˜์ ์ธ ์˜๋„๊ฐ€ ์—†๋‹ค๋ฉด ์–ด๊ธธ ์ผ์ด ์—†๋‹ค.

๋Œ€์นญ์„ฑ

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;
    }
}

class Main {

    public static void main(String[] args) {
        CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
        String s = "polish";

        // ๋Œ€์นญ์„ฑ ์œ„๋ฐฐ
        System.out.println(cis.equals(s)); // true
        System.out.println(s.equals(cis)); // false, ๋‹ค๋ฅธ ํƒ€์ž…์ด๊ธฐ ๋•Œ๋ฌธ์— false
    }
}

์ถ”์ด์„ฑ

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;
    }
}

class ColorPoint extends Point {

    private final Color color;

    public ColorPoint(int x, int y, Color color) {
        super(x, y);
        this.color = color;
    }

    @Override
    public boolean equals(Object o) {
        // ๊ตฌํ˜„ ๋‚ด์šฉ
    }
}

์œ„์˜ ColorPoint ํด๋ž˜์Šค ๋‚ด์˜ equals ๊ตฌํ˜„ ๋‚ด์šฉ์— ๋”ฐ๋ผ ๊ทœ์•ฝ ์œ„๋ฐฐ ์—ฌ๋ถ€๊ฐ€ ๊ฒฐ์ •๋œ๋‹ค. ์šฐ์„  ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜๋ฉด ๋Œ€์นญ์„ฑ์ด ์œ„๋ฐฐ๋œ๋‹ค.

class ColorPoint extends Point {
    // ...
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof ColorPoint)) {
            return false;
        }
        return super.equals(o) && ((ColorPoint) o).color == color;
    }
}

class Main {

    public static void main(String[] args) {
        Point p = new Point(1, 2);
        ColorPoint cp1 = new ColorPoint(1, 2, Color.RED);

        // ๋Œ€์นญ์„ฑ ์œ„๋ฐฐ
        System.out.println(p.equals(cp1)); // true
        System.out.println(cp1.equals(p)); // false
    }
}

์ด๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ Point ํด๋ž˜์Šค์— ๋Œ€ํ•œ ๋น„๊ต๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋Œ€์นญ์„ฑ์€ ์ง€์ผœ์ง€์ง€๋งŒ, ์ถ”์ด์„ฑ์ด ์œ„๋ฐฐ๋œ๋‹ค.

class ColorPoint extends Point {
    // ...
    @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;
    }
}

class Main {

    public static void main(String[] args) {
        Point p = new Point(1, 2);
        ColorPoint cp1 = new ColorPoint(1, 2, Color.RED);
        ColorPoint cp2 = new ColorPoint(1, 2, Color.BLUE);

        // ์ถ”์ด์„ฑ ์œ„๋ฐฐ
        System.out.println(p.equals(cp1)); // true
        System.out.println(cp1.equals(cp2)); // false
        System.out.println(p.equals(cp2)); // true
    }
}

๋งŒ์•ฝ instanceof ๋Œ€์‹  getClass๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ถ”์ด์„ฑ์€ ์ง€์ผœ์ง€์ง€๋งŒ, ๋Œ€์นญ์„ฑ์ด ์œ„๋ฐฐ๋œ๋‹ค. ๋˜ํ•œ Point์˜ ํ•˜์œ„ํด๋ž˜์Šค์ธ ColorPoint๊ฐ€ ๋”์ด์ƒ Point๋กœ์จ ์‚ฌ์šฉ๋  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค.(๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™ ์œ„๋ฐฐ)

class ColorPoint extends Point {
    // ...
    @Override
    public boolean equals(Object o) {
        if (o == null || o.getClass() != getClass()) {
            return false;
        }
        ColorPoint cp = (ColorPoint) o;
        return super.equals(o) && cp.color == color;
    }
}

class Main {

    public static void main(String[] args) {
        Point p = new Point(1, 2);
        ColorPoint cp1 = new ColorPoint(1, 2, Color.RED);

        // ๋Œ€์นญ์„ฑ ์œ„๋ฐฐ
        System.out.println(p.equals(cp1)); // true
        System.out.println(cp1.equals(p)); // false
    }
}

์ด์™€ ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” equals ๊ทœ์•ฝ์„ ์ง€ํ‚ค๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์šฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

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 point;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof ColorPoint)) {
            return false;
        }
        ColorPoint cp = (ColorPoint) o;
        return cp.point.equals(point) && cp.color == color;
    }
}

์œ„ ๋ฐฉ๋ฒ•์œผ๋กœ equals ๊ทœ์•ฝ์„ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ์ง€๋งŒ, ColorPoint๋ฅผ Point์™€๋Š” ๋”์ด์ƒ ์ƒ์† ๊ด€๊ณ„๋Š” ์•„๋‹ˆ๊ฒŒ ๋œ๋‹ค.

์ผ๊ด€์„ฑ

equals์˜ ํŒ์•ˆ๋ฐ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ์ž์›์ด ๋ผ์–ด๋“ค์ง€ ์•Š๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

null-์•„๋‹˜

instanceof ์—ฐ์‚ฐ์ž๋กœ ์ž…๋ ฅ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ํƒ€์ž…์ธ์ง€ ํ™•์ธํ•˜๋ฉด ๋ช…์‹œ์ ์œผ๋กœ null ๊ฒ€์‚ฌ๋ฅผ ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ์ž…๋ ฅ์ด null์ด๋ฉด ํƒ€์ž… ํ™•์ธ ๋‹จ๊ณ„์—์„œ false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ null ๊ฒ€์‚ฌ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

class Test {

    // ...

    // ๋ช…์‹œ์  null ๊ฒ€์‚ฌ
    @Override
    public boolean equals(Object o) {
        if (o == null) {
            return false;
        }
        // ...
    }

    // ๋ฌต์‹œ์  null ๊ฒ€์‚ฌ
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Test)) {
            return false;
        }
        // ...
    }
}

equals ๋ฉ”์„œ๋“œ ๊ตฌํ˜„์‹œ ์ฃผ์˜์‚ฌํ•ญ

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

    • ์ž๊ธฐ ์ž์‹ ์ด๋ฉด true๋ฅผ ๋ฐ˜ํ™˜

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

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

    • ๊ฐ€๋” ํ•ด๋‹น ํด๋ž˜์Šค๊ฐ€ ๊ตฌํ˜„ํ•œ ํŠน์ • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋น„๊ตํ•  ์ˆ˜๋„ ์žˆ์Œ

    • ์ด๋Ÿฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ผ๋ฉด equals์—์„œ (ํด๋ž˜์Šค๊ฐ€ ์•„๋‹Œ) ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•จ

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

    • 2๋ฒˆ์—์„œ instanceof ์—ฐ์‚ฐ์ž๋กœ ์ž…๋ ฅ์ด ์˜ฌ๋ฐ”๋ฅธ ํƒ€์ž…์ธ์ง€ ๊ฒ€์‚ฌ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋‹จ๊ณ„์—์„  ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ

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

    • ๋ชจ๋‘ ์ผ์น˜ํ•ด์•ผ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ตฌํ˜„

  5. ๊ธฐ๋ณธ ํƒ€์ž…์€ ==๋กœ ๋น„๊ตํ•˜๊ณ  ์ฐธ์กฐํƒ€์ž…์€ equals๋กœ ๋น„๊ต

  6. float, double ํ•„๋“œ๋Š” ์ •์  ๋ฉ”์„œ๋“œ Float.compare(float, float)์™€ Double.compare(double, double)๋กœ ๋น„๊ต

    • Float.equals(float)๋‚˜ Double.equals(double)๋Š” ์˜คํ†  ๋ฐ•์‹ฑ์„ ์ˆ˜๋ฐ˜ํ•ด ์„ฑ๋Šฅ์ƒ ์ข‹์ง€ ์•Š์Œ

  7. ๋ฐฐ์—ด ํ•„๋“œ๋Š” ์›์†Œ ๊ฐ๊ฐ์„ ์ง€์นจ๋Œ€๋กœ ๋น„๊ต

    • ๋ชจ๋‘๊ฐ€ ํ•ต์‹ฌ ํ•„๋“œ๋ผ๋ฉด Arrays.equals()๋ฅผ ์‚ฌ์šฉ

  8. NullPointException ๋ฐœ์ƒ์„ ์˜ˆ๋ฐฉํ•˜๊ธฐ ์œ„ํ•ด Object.equals(object, object)๋กœ ๋น„๊ต

  9. ํ•„๋“œ์˜ ๋น„๊ต ์ˆœ์„œ๋ฅผ ์ž‘์€ ๋น„์šฉ์ด ๋“œ๋Š” ํ•„๋“œ๋ถ€ํ„ฐ ํฐ ๋น„์šฉ์ด ๋“œ๋Š” ํ•„๋“œ ์ˆœ์œผ๋กœ ๋น„๊ต

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

Last updated

Was this helpful?