아이템 34. int 상수 대신 열거 타입을 사용하라 enum type(열거 타입)은 일정 개수의 상수 값을 저장하고 그 외의 값은 허용하지 않는다. public static final int 김치 = 0; public static final int 찌개 = 1; public static final int 된장 = 2; // 프로젝트하다 본 상수 타입 (자바 7 버전임) public static final int STATUS_SUCCESSFUL = 0; public static final int STATUS_MISSING_DATA = 1; public static final int STATUS_NOT_DETECTED = 2; enum type이 지원되기 이전에는 위와 같이 정수 타입을 열거하여 사용했다...
32. 제네릭과 가변인수를 함께 쓸 때는 신중하라 가변인수(varags)가 무엇인가요? private static String[] check(String... arr){ return arr; } 이 친구이다. 메소드의 파라미터로 여러개의 변수를 받을 수 있게 만들어 주는 기능이다. 그렇다면 왜 제네릭과 쓸 때 주의해야 하느냐? 바로 가변인수로 받은 파라미터들을 배열을 생성해 담기 때문이다. 아아.. 배열.. 이미 지난 아이템에서 부터 제네릭과 배열의 안 좋은 궁합에 대해 입이 닳도록 이야기해 왔다. // 제네릭 varargs 배열 매개변수에 값을 저장하는 것은 안전하지 않다. (191-192쪽) public class Dangerous { // 코드 32-1 제네릭과 varargs를 혼용하면 타입 안전성..
28. 배열보다는 리스트를 사용하라. 아이템 26에서 배열과 리스트의 차이에 공변(convariant)과 불공변(invariant)에 대해 얘기 했다. Object와 String이 부모 자식 관계이지만 List에 List을 대입할 수 없다. 그러나 배열의 경우 String[]을 Object[]로 타입 캐스팅이 가능하다. 이것이 공변과 불공변의 차이이다. Object[] objArr = new Long[1]; objArr[0] = "zz"; // -> 여기서 런타임 에러 List ol = new ArrayList() // -> 여기서 컴파일 에러 ol.add("zz"); 이런 실수를 배열의 경우 런타임에 알지만 리스트는 컴파일 타임에 알 수 있다. 배열과 리스트의 또 다른 차이점은 배열은 실체화(reify..
26. 로 타입은 사용하지 말라 갑자기 로 타입이라고 하길래 이 친구가 무슨 녀석인지 부터 알 필요가 있다. 쉽게 말해 제네릭 타입 List의 로(raw) 타입은 List가 된다. 클래스의 매개 변수를 제네릭을 통해 특정 클래스로 제안하지 않으면 발생할 문제들이 눈에 선하다 제네릭이 생기까지는 10년의 세월이 흘러 기존의 코드와의 호환성을 위해 raw 타입을 남겨뒀다고한다. // Stamp 클래스만 취급합니당 ^^7 private final Collection stamps = ...; stamps.add(new Coin()); 위와 같은 Collection을 raw 타입으로 선언하고 주석으로 열심히 Stamp 객체만 달라고 해도 무시하고 Coin을 넣는다. 컴파일러는 경고를 unchecked call 경..
24. 멤버 클래스는 되도록 static으로 만들어라 중첩 클래스(nested class)는 다른 클래스 내부에 정의된 클래스를 말한다. nested class는 해당 클래스가 선언 된 클래스 내부에서만 사용해야하며 그렇지 않다면 그냥 밖에다 만들어야한다. nested class의 종류에는 static member classes, nonstatic member classes, anonymous classes, local classes가 있다. static과 nonstatic은 익히 봐 왔으니 넘어가고 anonymous classes와 local classes에 대해 살펴보겠다. anonymous classes는 아래의 코드를 보면 이해가 쉽게 간다. public class Example { interfa..
19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라 아이템 18에서 상속을 염두해 두지 않은 메소드 설계의 위험성을 보았다. public과 protected 메소드 중 final로 설정하지 않아 재정의가 가능한 메소드들은 상속시에 발생할 수 있는 문제들을 지니고있다. 이러한 모든 메소드들에 대해서는 상속을 염두해 둔 문서화 작업이 필요하다. 이 메소드는 이러 이러한 함수를 사용해서 이러 이렇게 동작한다와 같이 말이다. 그러나 이는 사용자가 API의 내부 구현을 몰라도 되고 사용만 하면 된다는 캡슐화의 내용과 상이하다. 그렇다 상속은 바로 이 캡슐화를 해치기 때문에 상속이 가능한 클래스라면 이러한 문서화가 필수적이다. 그렇지 않다면 상속을 금해야한다. 상속용 클래스에서 어떤 메소드를..
16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 클래스 내부의 field에 접근하기 위해 이 필드를 public으로 열어 직접 접근하게 하는것이 아닌 private으로 선언하고 이에 접근하는 메소드를 제공해야한다. // 코드 16-2 접근자와 변경자(mutator) 메서드를 활용해 데이터를 캡슐화한다. (102쪽) class Point { private double x; private double y; public Point(double x, double y) { this.x = x; this.y = y; } public double getX() { return x; } public double getY() { return y; } public void setX(doub..
13. clone 재정의는 주의해서 진행하라 Object는 protected 메소드인 clone을 가지고 있다. 이 메소드를 재정의 하기 위해서는 Cloneable interface를 구현해야한다. Cloneable interface에는 어떠한 메소드도 정의 되어 있지 않지만, 이를 구현한 클래스에서 clone을 호출하면 해당 클래스의 필드들을 하나하나 복사한 인스턴스를 새로 반환해 준다. 필드들이 primitive 타입만 존재한다면 Cloneable이 만들어 준 clone을 그대로 사용해도 되지만 reference 타입의 필드가 존재하면 deep copy가 이루어지지 않아 clone을 재정의 해주어야한다. public final class PhoneNumber implements Cloneable { ..
eqauls를 재정의한 클래스는 hashCode 또한 재정의 해야 한다. 이는 HashMap이나 HashSet과 같은 컬렉션의 원소 비교방법 때문이다. public final class PhoneNumber { private final short areaCode, prefix, lineNum; public PhoneNumber(int areaCode, int prefix, int lineNum) { this.areaCode = rangeCheck(areaCode, 999, "area code"); this.prefix = rangeCheck(prefix, 999, "prefix"); this.lineNum = rangeCheck(lineNum, 9999, "line num"); } private stat..
Java로 알고리즘 풀 때 Overriding 해 본 equals이다. equals를 재정의 하면 안되는 조건에 대해 소개해준다. 각 인스턴스가 본질적으로 고유하다. Object는 기본적으로 equals 메소드의 비교를 레퍼런스가 같은지를 확인한다. Thread와 같이 값을 표현하는 것이 아닌 동작하는 개체를 표현하는 클래스는 재정의 하지 않는 것이 좋다. 인스턴스의 논리적 동치성(logical equality)을 검사할 일이 없다. 두 인스턴스가 물리적으로 같은지가 아닌 논리적으로 같은지를 확인해야 할 때 재정의가 필요하다. 클라이언트 코드에서 이를 호출할 일도 없고 필요도 없다면 굳이 재정의할 필요가 없다. 상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어맞는다. 설계를 완벽하게 해서 ..