item14 - Comparable을 구현할지 고민하라
comparable은 데이터의 순서를 정렬하는데 있어서 그 순서를 정해주는 인터페이스이다 주로 자연데이터 이외에 객체같은 것의 순서를 정리할 때나, 순서를 사용자 정의를 통해서 진행해야하는 경우에 사용된다
이 인터페이스는 제네릭 타입을 지원하기 떄문에 컴파일 시점에 타입이 찍히게 된다 이퀄스랑 비슷한 성질을 가지고 있다고 볼 수 있다 comparable 인터페이스에서 정의할 수 있는 compareTo를 생성해서 사용하는데 몇 가지 규약이 존재한다
구현할때 고민해볼만한 규약
첫 번째로는 반사성이다 -> A.compareTo(A) 는 성립해야 한다 두 번째로는 추이성이다 -> A>B 이고 B>C이면 A>C이 성립해야 한다 세 번째로는 일괄성이다 -> A=B 일때 A=C의 결과와 B=C의 결과가 항상 같은 결과가 나와야 한다 마지막으로는 지켜지면 좋을 그러한 규약이다 마지막은 compareTo의 결과 값과 equals의 결과 값이 같으면 좋다 -> BigDecimal 타입은 다른 값이 나오지만 javaDocs에서 보면 다른 이유에 대해서 작성해두었다 이렇게 굳이 같지는 않아도 지장은 없지만 같으면 가시적으로도 여러가지 측면에서 좋을 것이라고 보인다
어떻게하면 구현할까?
implements Comparatable 선언하고 compareTo 을 overriding해서 내부에서 로직을 작성해 나간다 내부의 구현에서 기본 타입은 박싱된 기본 타입(Integer, String 등등)의 compare을 사용해서 비교하면 좋다. 그리고 핵심 필드가 여러 개라면 비교하는 순서가 중요하기 떄문에 중요한 필드 순서로 비교를 진행하면 좋다 그리고 clone()을 사용하는 것 처럼 다형성의 특징에 의해서 만약 부모 클래스에서 compare을 재구현하고 다시 자식 클래스에서 또 compare을 구현하고자 implements받고 재구현하면 안된다 그렇게 하는 것이 아니라 자식 클래스를 타입으로 가진 TreeSet을 통해서 compare을 재구현해주면 된다 물론 이것도 아주 귀찮은 방법이기 떄문에 기존에 equals에서는 composition을 사용하라고 했었다 즉, 상속받는 개념이 아니라, 해당 객체를 필드로 가지고 있는 새로운 객체를 생성해서 다시 compare을 구현하는 방식이다
또 다른 방법으로는 Comparator에서 제공해주는 static 메소드를 사용해서 만드는 것도 가능하다 private static final Comparator<타입> COMPARATOR = comparingInt(등등).thenCompare(등등) 이렇게 구현하두고 COMPAREATOR 메소드를 통해서 comparator 인스턴스를 하나 생성해주고 그 이후에 compareTo을 통해서 비교하는 방식이다 이게 보면, 결국은 implement을 통해서 그 클래스에서 구현하는 방식과 그냥 인스턴스를 생성하는 방식이라고 보면 된다 이렇게 사용하는 곳에서 직접 구현해서 사용하면 장점으로는 가독성이 좋아진다? 가 있다 단점으로는 성능이 10%정도 느려진다고 한다 뭐 그렇게 큰 이슈는 아니라고 보이긴하는디... 이것도 방법인 것 같다
핵심공략
제네릭 인터페이스는 컴파일 시점에 타입을 확정짓는다 여기서 컴파일 타임이란 실제로 실행하기 전을 의미이다. 그래서 런타임에서 나는 에러보다는 컴파일타임에서 에러를 찾아야한다 런타임에서 나는 에러는 실제로 찾기 힘들다는 것이다 그렇기 때문에 우리가 본 Comparable 같은 경우에는 implements하면서 타입을 지정해주기 때문에 이 타입에 따라서 어떻게 정렬하는지에 대해서 구현하는데 있어서 컴파일 시점에 에러를 찾을 수 있어서 좋다 정수overflow 란 -> 정수의 가장 작은 수치에다가 정수를 마이너스 해버리면 다시 가장 큰 정수로 돌아가기 마련이다 그래서 그냥 연산자를 사용하지 말고 Integer.compare()을 사용해서 결고를 처리하자 그리고 부동 소수점에 대해서는 계산하는데 있어서 이슈가 항상 있을 수 있어 위험하기 때문에 BigDecimal을 사용하는게 확실하게 사용하는 것이 좋다
Last updated
Was this helpful?