2023-java-study / book-study

북 스터디 기록 레포지토리
0 stars 0 forks source link

[Item 14] 기존 클래스를 확장한 구체 클래스에서의 compareTo 규약 #49

Closed gmelon closed 1 year ago

gmelon commented 1 year ago

p.89에 기존 클래스를 확장한 구체 클래스에서 새로운 값 컴포넌트를 추가했다면 compareTo 규약을 지킬 방법이 없다 라고 했는데 이 부분 직관적으로 와닿지가 않아서 이에 대한 간단한 예시 코드가 궁금합니다

ssstopeun commented 1 year ago

아이템 10을 참고해 작성해보았습니다.

public class Point implements Comparable<Point> {
    protected Integer x;
    protected Integer y;

    public Point(Integer x, Integer y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public int compareTo(Point point) {
        int result = Integer.compare(x, point.x);
        if (result == 0) {
            return Integer.compare(y, point.y);
        }
        return result;
    }
}

public class ColorPoint extends Point implements Comparable<Point>{
    private Integer color;

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

    @Override
    public int compareTo(Point point) {
        int result = super.compareTo(point);
        if (result == 0) {
            return Integer.compare(color, ((ColorPoint) point).color); // 잘못된 구현
        }
        return result;
    }
}
public class PointTest {
    public static void main(String[] args) {
        Point point = new Point(1,2);
        ColorPoint colorPoint = new ColorPoint(1,2,3);

        System.out.println(point.compareTo(colorPoint));
        System.out.println(colorPoint.compareTo(point));
    }
}

image

이렇게 잘못된 다운 캐스팅이 되면 ClassCastException이 됩니다. 아이템 10. equals는 일반 규약을 지켜 정의하라 에도 보면 구체 클래스를 확장해 새로운 값을 추가하면서 equals 규약을 만족시킬 방법은 존재하지 않는다. 고 나옵니다.

이를 해결하기 위해 뒤에 나오는 말처럼 이 클래스에 원래 클래스의 인스턴스를 가리키는 필드를 두자. 그런 다음 내부 인스턴스를 반환하는 '뷰' 메서드를 제공하면 된다. 를 구현해보면

class ColorPoint implements Comparable<ColorPoint> {
    private Point point;
    private int color;

    public ColorPoint(Point point, int color) {
        this.point = point;
        this.color = color;
    }

    public Point asPoint() {
        return point;
    }

    @Override
    public int compareTo(ColorPoint colorPoint) {
        int result = point.compareTo(colorPoint.point);
        if (result == 0) {
            return Integer.compare(color, colorPoint.color);
        }
        return result;
    }
}

이렇게 중간에 asPoint로 원래 클래스인 Point의 인스턴스를 가리키도록 한 후에 테스트하게 되면 원하는 값을 얻을 수 있습니다.

public class Main {
    public static void main(String[] args) {
        Point point = new Point(1,2);
        ColorPoint colorPoint = new ColorPoint(new Point(1,2),3);

        System.out.println(point.compareTo(colorPoint.asPoint()));
        System.out.println(colorPoint.asPoint().compareTo(point));
    }
}

image