본문 바로가기
개발 공부/Java

이펙티브 자바 아이템 14 - Comparable 을 구현할지 고민하라 - 완벽 공략

by 개발인생 2022. 10. 31.
반응형

아이템 14 - Comparable 을 구현할지 고민하라 - 완벽 공략

이 글은 백기선 님의 이펙티브 자바 강의와 이펙티브 자바 3 / E 편을 참고하여 작성하였습니다.

compareTo 메서드의 인수 타입은 컴파일타임에 정해진다.

제네릭 인터페이스에 제네릭 타입을 선언해주면 구체적인 타입을 컴파일 타임에 인수타입이 정해진다.

컴파일 타임 은 작성한 코드가 컴파일 되는 시점을 의미한다.

어떠한 경우든 컴파일 타임 에 문제를 찾는게 더 좋다.

런타임 에서 문제가 발생했다는 것은 문제를 발견하는데까지 시간이 오래걸린다는 의미다.

런타임 에서 발생하는 문제를 해결하는 게 더 까다롭다.

Equals 같은 경우 해당 타입이 맞지 않는다는 오류는 런타임 에서 발생한다.

실제로 어떤 오브젝트가 메서드에 들어올지는 런타임 에서 알 수 있기 때문이다.

제네릭 인터페이스 같은 경우는 지원할 타입을 명시 하기 때문에 컴파일 타임 에서 타입확인이 가능하다.

자바의 타입 추론 능력

   private static final Comparator<PhoneNumber> COMPARATOR =
         comparingInt((PhoneNumber pn) -> pn.areaCode) // 타입 추론을 하지 못해 타입을 알려주어야한다.
               .thenComparingInt(pn -> pn.prefix)
               .thenComparingInt(pn -> pn.lineNum);

comparingInt 메서드 사용시 어떤 타입을 사용할지 코드에 알려주었다.

그 다음 코드부터는 자바에서 타입 추론을 했기때문에 더 이상 어떤 타입을 사용해야할지 알려줄 필요가없다.

var result = Short.compareTo(a1, a2);

어떠한 값을 담는 변수를 사용할 때 로컬에서 사용할 변수라면 선언할 때 위처럼 타입을 생략할 수 있다.

타입추론 적절히 사용하면 코드를 간결하게 작성할 수 있다.

타입 추론은 여러곳에서 (람다, 제네릭 등) 쓰일 수 있다.

정수 오버플로

public class main {
   public static void main(String[] args) {
      System.out.println(-2147483648 -10);
   }
}

위처럼 정수의 범위를 벗어나는 연산에서 의도한 값이 나오지 않을 떄가 있다.

위의 결과는 음수가 나오길 바라지만 양수가 나오는 경우다.

즉, compareTo 에서 현재의 값에서 비교하는 값을 빼는 연산을 하면 위와같이 의도하지 않은 값이 나올 수도 있다.

public class main {
   public static void main(String[] args) {
      System.out.println(Integer.compare(-2147483648, -10));
   }
}

권장하는 방법은 위의 코드처럼 박싱타입의 compare 메서드를 사용하는 것이다.

또한 (x < y) ? -1 : ((x == y) ? 0 : 1) 과 같이 우리가 직접 부등호를 사용하는 것은 권장하지 않는다.

코드를 읽기도 힘들고, 오류가 발생할 가능성이 높아지기 때문이다.

부동소수점

부동소수점 자체는 계산이 정확하지가 않는다.

가수부를 2진수로 표현하다가 표현할 수 있는 메모리 공간을 넘어가게 되면 계산된 결과가 부정확하게 나온다.

public class main {
   public static void main(String[] args) {
      int i = 1;
      double d = 0.1;

      System.out.println(i - d * 9);
   }
}

위의 결과값은 0.1 이 아니라 0.1 에서 약간 모자란 값이 나온다.

import java.math.BigDecimal;

public class main {
   public static void main(String[] args) {
      int i = 1;
      double d = 0.1;
      System.out.println(i - d * 9);

      BigDecimal bd = BigDecimal.valueOf(0.1);
      System.out.println(BigDecimal.valueOf(1).min(bd.multiply(BigDecimal.valueOf(9))));
   }
}

정확한 계산이 필요한 경우에는 소수점 계산을 내부적으로 int 로 만들어 사용하는 BigDecimal 사용을 권장한다.

금융권 등과 같이 정확한 계산이 필요한 곳에서는 반드시 BigDecimal 을 사용해야한다.

반응형

댓글