본문 바로가기

독서찰기(讀書札記)

(90)
[아이템 62] 다른 타입이 적절하다면 문자열 사용을 피하라 [배경] 문자열은 워낙 흔하고 자바가 또 잘 지원해주어서 원래 의도하지 않은 용도로도 쓰이는 경향이 있다. [Why] 문자열은 다른 값 타입을 대신하기에 적합하지 않다. 많은 사림이 파일, 네트워크, 키보드 입력으로부터 데이터를 받을 때 주로 문자열을 사용한다. 하지만 입력받을 데이터가 진짜 문자열일 때만 그렇게 하는 게 좋다. 문자열은 열거 타입을 대신하기에 적합하지 않다. (아이템34) 문자열은 혼합 타입을 대신하기에 적합하지 않다. String compoundKey = className + "#" + i.next(); 이는 단점이 많은 방식이다. 두 요소를 구분해주는 문자 #이 두 요소 중 하나에서 쓰였다면 혼란스러운 결과를 초래한다. 각 요소를 개별로 접근하려면 문자열을 파싱해야 해서 느리고, 귀..
[아이템 61] 박싱된 기본 타입보다는 기본 타입을 사용하라 [배경] 기본 타입과 박싱된 기본 타입의 주된 차이는 크게 세 가지다. 첫 번째, 기본 타입은 값만 가지고 있으나, 박싱된 기본 타입은 값에 더해 식별성(identity)이란 속성을 갖는다. 두 번째, 기본 타입의 값은 언제나 유효하나, 박싱된 기본 타입은 유효하지 않은 값, 즉 null을 가질 수 있다. 세 번째, 기본 타입이 박싱된 기본 타입보다 시간과 메모리 사용면에서 더 효율적이다. [Why] (같은 객체를 비교하는 게 아니라면) 박싱된 기본 타입에 == 연산자를 사용하면 오류가 일어난다. naturalOrder.compare(new Integer(42), new Integer(42)) 첫 번째 검사인 (i < j)는 잘 작동하지만, 두 번째 검사인 (i == j)에서는 두 '객체 참조'의 식별성..
[아이템 60] 정확한 답이 필요하다면 float와 double은 피하라 [예시] 주머니에 1.03달러가 있었는데 그중 42센트를 썼다고 해보자. System.out.println(1.03 - 0.42); 안타깝게도 이 코드는 0.6100000000000001을 출력한다. 주머니에 1달러가 있었는데 10센트짜리 사탕 9개를 샀다고 해보자. System.out.println(1.00 - 9 * 0.10); 이 코드는 0.09999999999999998을 출력한다. 주머니에는 1달러가 있고, 선반에 10센트, 20센트, 30센트, ... 1달러짜리의 맛있는 사탕이 놓여 있다고 해보자. 10센트짜리부터 하나씩, 살 수 있을 때까지 사보자. 사탕을 몇 개나 살 수 있고, 잔돈은 얼마가 남을까? 결과값을 출력하기 전에 반올림하면 해결되리라 생각할지 모르지만, 반올림을 해도 틀린 답이 ..
[아이템 59] 라이브러리를 익히고 사용하라 [Why] "바퀴를 다시 발명하지 마라." static Random rnd = new Random(); static int random(int n) { return Math.abs(rnd.nextInt()) % n; } 괜찮은 듯 보여도 문제를 세 가지나 내포하고 있다. 첫 번째, n이 그리 크지 않은 2의 제곱수라면 얼마 지나지 않아 같은 수열이 반복된다. 두 번째, n이 2의 제곱수가 아니라면 몇몇 숫자가 평균적으로 더 자주 반환된다. n값이 크면 이 현상은 더 두드러진다. 세 번째, 지정한 범위 '바깥'의 수가 종종 튀어나올 수 있다. rnd.nextInt()가 반환한 값을 Math.abs를 이용해 음수가 아닌 정수로 매핑하기 때문이다. nextInt()가 Integer.MIN_VALUE를 반환하면..
[아이템 58] 전통적인 for 문보다는 for-each 문을 사용하라 [Why] 스트림이 제격인 작업이 있고 반복이 제격인 작업이 있다. for (Iterator i = c.iterator(); i.hasNext();) { Element e = i.next(); ... // e로 무언가를 한다. } for (int i=0; i
[아이템 57] 지역변수의 범위를 최소화하라 [Why] 지역변수의 유효 범위를 최소로 줄이면 코드 가독성과 유지보수성이 높아지고 오류 가능성은 낮아진다. [How] 1. 지역변수의 범위를 줄이는 가장 강력한 기법은 역시 '가장 처음 쓰일 때 선언하기'다. 사용하려면 멀었는데, 미리 선언부터 해두면 코드가 어수선해져 가독성이 떨어진다. 변수를 실제로 사용하는 시점엔 타입과 초깃값이 기억나지 않을 수도 있다. 2. 거의 모든 지역변수는 선언과 동시에 초기화해야 한다. 초기화에 필요한 정보가 충분하지 않다면 충분해질 때까지 선언을 미뤄야 한다. try-catch문은 이 규칙에서 예외다. 변수를 초기화하는 표현식에서 검사 예외를 던질 가능성이 있다면 try 블록 안에서 초기화해야 한다. (그렇지 않으면 예외가 블록을 넘어 메서드에까지 전파된다) 변수 값을..
[아이템 55] 옵셔널 반환은 신중히 하라 [배경] 자바 8 전에는 메서드가 특정 조건에서 값을 반환할 수 없을 때 취할 수 있는 선택지가 두 가지였다. 첫 번째는, 예외를 던지는 것이었다. 하지만 예외는 진짜 예외적인 상황에서만 사용해야 한다.(아이템69) 또한 예외를 생성할 때 스택 추적 전체를 캡처하므로 비용도 만만치 않다. 두 번째는, null을 반환하는 것이었다. 하지만 null을 반환할 수 있는 메서드를 호출할 때는 별도의 null 처리 코드를 추가해야 한다. 자바 8로 올라가면서 또 하나의 선택지가 생겼다. 그 주인공인 Optional는 null이 아닌 T 타입 참조를 하나 담거나, 혹은 아무것도 담지 않을 수 있다. 아무것도 담지 않은 옵셔널은 '비었다'고 말한다. 반대로 어떤 값을 담은 옵셔널은 '비지 않았다'고 한다. 옵셔널은 ..
[아이템 54] null이 아닌, 빈 컬렉션이나 배열을 반환하라 [Why] private final List cheesesInStock = ...; /** * @return 매장 안의 모든 치즈 목록을 반환한다. * 단, 재고가 하나도 없다면 null을 반환한다. */ public List getCheeses() { return cheesesInStock.isEmpty() ? null : new ArrayList(cheesesInStock); } List cheeses = shop.getCheeses(); if (cheeses != null && cheeses.contains(Cheese.STILTON)) { System.out.println("좋았어, 바로 그거야."); } 컬렉션이나 배열 같은 컨테이너가 비었을 때 null을 반환하는 메서드를 사용할 때면 항상 방..