본문 바로가기

프로그래밍

[이펙티브 자바] 챕터 3 - 공통 메서드

Chapter 3. 객체 공통 메서드

Item8. equal Override

equal override 안할 때

  • 객체가 생성될 때마다 유니크할 때
  • equal이 중요하지 않을 때
  • 부모 클래스가 이미 적합한 override 했을 때
  • private 클래스내에서 equal 메서드가 안 쓰일 때

equals override 하는 상황

  • 주로 value class와 같은 논리적 동등의 성격이 있을 때 사용한다.

    value class란? - IntegerDate 클래스처럼 대표값을 지니고 있는 클래스.

equals override 규칙

x, y, z가 null이 아닐 때

  1. reflexive : x.equals(x) 는 항상 true를 리턴.
  2. Symmetric : x.equals(y)y.equals(x) 의 리턴값은 같아야 한다.
  3. transitive : x.equals(y)y.equals(z)true이면 x.equals(z)true여야 한다.
  4. x.equals(null) 은 항상 false다.

클래스 확장과 equals

결론부터 말하면 상속을 통한 확장으로 equals를 확장해 나가는 것은 불가능하다. 예를 들면 x,y,z 좌표를 갖고 있는 3차원 점과 x,y 좌표를 갖고 있는 2차원 점과 equals를 구현하는 방법은 없다.

    private static class Point2D {
        final int x;
        final int y;

        public Point2D(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public boolean equals(Object object) {
            if (!(object instanceof Point2D))
                return false;
            Point2D point = (Point2D) object;
            return point.x == this.x && point.y == this.y;
        }
    }

    private static class Point3D extends Point2D {
        private final int z;

        public Point3D(int x, int y, int z) {
            super(x, y);
            this.z = z;
        }

        @Override
        public boolean equals(Object object) {
            if (!(object instanceof Point2D)) {
                return false;
            }
            if (object instanceof Point3D) {
                Point3D point = (Point3D) object;
                return this.x == point.x && this.y == point.y
                        && this.z == point.z;
            }
            Point2D point = (Point2D) object;
            return this.x == point.x && this.y == point.y;
        }

    }

    public static void main(String[] args){
        Point2D point1 = new Point2D(0, 0);
        Point3D point2 = new Point3D(0, 0, 0);
        Point3D point3 = new Point3D(0, 0, 1);

        System.out.println(point1.equals(point2));
        System.out.println(point2.equals(point1));
        // reflexive 만족

        System.out.println(point2.equals(point1));
        System.out.println(point1.equals(point3));
        System.out.println(point2.equals(point3));
        // transitivity 불만족
    }

아무리 고민해도 equals의 규약을 만족시키면서 2차원과 3차원 점을 비교할 수 있는 방법은 잘 나오지 않는다.

상속보다는 composition

2차원 점과 3차원 점에서 equals의 문제는 point2D.equals(point3D) 하면 point3D를 point2D로 타입캐스팅해서 point2D로서 서로를 비교하는 점이다. 이걸로 인해 reflexive를 만족하기 위해서 point3D에서도 point2D랑 비교할 때 따로 분기처리를 해줘야 했다. 만약, composition을 통해 Point3D를 설계를 한다면 point2D.equals(point3D) 가 항상 false로 나올 것이다. 다만, abstract 클래스를 상속받는 클래스의 경우, 부모 클래스의 객체가 생성될 가능성이 없기 때문에 상속을 통해 확장하고, equals를 override 해도 괜찮다.

equals override 하는 방법

다음과 같은 순서로 코드를 짠다.

  1. ==로 argument가 this 객체의 reference를 갖고 있는지 체크.
  2. instanceof 를 통해 타입이 제대로 되었는지 체크.
  3. 타입 캐스팅
  4. 클래스의 ‘유의미’한 필드를 비교.
  5. symmetric, transitive, consistent 체크

주의할 점

  • equals override 시 hascode override를 반드시 해라.
  • argument로 obejct type 외에 다른 것을 넣지마라. (override가 안된다.)

Item 9. override hashcode()

hashcode 규약

  • 같은 객체에 hashcode() 호출시 객체에 변경 사항이 없다면 같은 숫자를 반환해야 한다.
  • equals에서 true를 반환하는 두 객체는 hashcode가 같아야 한다.
  • equals에서 false를 반환하는 두 객체의 hashcode가 다를 필요는 없다.

hashcode 만들 때 주의할 사항

  • 계속 같은 숫자 반환하는 hashcode 메서드를 만들 경우, 이 객체로 이뤄진 hashmap은 linked list가 되면서 성능 저하가 일어난다.
  • hashcode 계산이 복잡할 때는 lazy initialization과 caching을 활용하자.

Item 10. toString

toString은 객체의 유의미한 정보를 반환해야 한다.
toString의 포맷을 확정할지 말지를 정해야 하는데, 확정할 경우, 이후에 바꿔서는 안된다.

client 쪽에서 toString을 parsing해서 뭔가 할 수 있기 때문이다.

toString이 반환하는 정보에 접근할 수 있는 방법을 제공해야 한다.

예를 들면, 시간에 년월일을 toString으로 반환하는데, 년, 월, 일에 접근할 수 있는 메서드가 제공되지 않으면 클라이언트 쪽에서 강제로 toString으로 반환되는 문자열을 파싱해서 접근해야 하기 때문이다.


Item 11. clone

override 하지 마세요.

  • clone의 메서드를 override 하려면 Cloneable을 implement 해야한다. (Object 의 메서드인데… ;;;)
  • clone은 생성자를 호출하지 않는다.
  • 현실에서 자바 프로그래머들은 super.clone()을 하면 subclass 타입의 객체가 반환되기를 기대하는데, 이렇게 하는 방법은 Object의 clone()이 호출되는 것이다.
  • clone을 실행하는 객체가 final mutable 객체를 field 변수로 갖고 있으면 제대로 clone하는 방법이 없다.

'프로그래밍' 카테고리의 다른 글

coursera 스칼라 2주차  (0) 2015.05.18
coursera 스칼라 1주차  (0) 2015.05.18
[이펙티브 자바] 챕터 2  (0) 2015.04.07
자바스크립트 로딩  (0) 2014.10.31
프로그래머, 열정을 말하다.  (0) 2014.07.16