JAVA/이펙티브 자바
26. 로 타입을 사용하지 말라 ( 제네릭, 타입 안정성 )
제네릴은 자바 5부터 사용할 수 있다. 제네릭을 지원하기 전에는 컬렉션에서 객체를 꺼낼 때 마다 캐스팅을 해야 했다. 실수로 인한 캐스팅 오류가 런타임에 자주 나타났다. 제네릭을 사용하면 켈렉션이 담을 수 있는 타입을 컴파일러에 알려주게 된다. 때문에 타입이 달라서 발생하는 캐스팅 오류를 차단할 수 있고 안정성을 높일 수 있다. 용어. 캘래스의 인ㅌ페이스 선언에 타입 매개변수가 사용되면 이를 제네릭 클래스 혹은 제네릭 인터페이스라 부른다. List 인터페이스는 원소의 타입을 나타내는 타입 매개변수 E를 받는다. 그래서 인터페이스의 완전한 이름은 List이다. 제네릭 클래스와 제네릭 인터페이스를 통들어 제네릭 타입이라 한다. 각각의 제네릭 타입은 일련의 매개변수화 타입을 정의한다. 에 들어가는 타입이 셀제..
25. 톱레벨 클래스는 한 파일에 하나만 담으라
소스 파일 하나에 톱레벨 클래스를 여러 개 선언하더라도 자바 컴파일러는 불평하지 않는다. 하지만 아무런 득이 없을 뿐더러 심각한 위험을 감수해야 한다. 한 클래스를 여러 가지로 정의할 수 있고, 그중 어느 것을 사용할지는 어느 소스 파일을 먼저 컴파일하냐에 따라 달라지기 때문이다. // Utensil.java class Utensil { static final String NAME = "pan"; } class Dessert { static final String NAME = "cake"; } Utensil.java 파일에 정의 된 두개의 클래스의 예제이다. 그 다음 Dessert.java 파일에 같은 두 클래스를 담은 클래스를 만들었다. // Dessert.java class Utensil { stat..
24. 멤버 클래스는 되도록 static으로 만들라 ( 멤버 클래스,어댑터 패턴 )
중첩 클래스란 다른 클래스 안에 정의된 클래스를 말한다. 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다. 중첩 클래스는 정적 멤버, 비정적 멤버, 익명, 지역 클래스 네가지이다. 이 중 첫번째를 제외한 나머지는 내부 클래스에 해당한다. 정적 멤버 클래스 - 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스 비정적 멤버 클래스 - 바깥 클래스의 인스턴스와 암묵적으로 연결된다. - 어댑터를 정의할 때 자주 쓰인다. - 멤버 클래스에서 바깥 인스턴스를 참조할 필요가 없다면 무조건 정적 멤버 클래스로 만들자. 익명 클래스 - 바깥 클래스의 멤버가 아니며, 쓰이는 시점과 동시에 인스턴스가 만들어진다. - 비정적인 문맥에서 사용될 때..
23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라 ( 클래스 계층구조 )
태그 달린 클래스란 두가지 이상의 의미를 표현할 때 그 중 현재 표현하는 의미를 태그값으로 알려주는 클래스를 말한다. 태그 달린 클래스에는 여러 단점이 있다. public class Figure { enum Shape {RECTANGLE, CIRCLE} final Shape shape; // 태그필드 - 현재모양을 나타낸다. double length; double width; // 필드 모양이 사각형(RECTANGLE)일때만 쓰인다. double radius; // 필드 모양이 원(CIRCLE)일때만 쓰인다. public Figure(final double radius) { shape = Shape.CIRCLE; this.radius = radius; } // 원 생성자 public Figure(fina..
22. 인터페이스는 타입을 정의하는 용도로만 사용하라 ( 상수 공개 )
인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다. 달리 말하면 클래스가 어떤 인터페이스를 구현한다는 것은 자신의 인스턴스를 무엇을 할 수 있는지를 클라이언트에게 애기해주는 것이다. 인터페이스는 반드시 위 용도로만 사용이 되어야 한다. 상수 인터페이스는 메서드없이 상수를 뜻하는 static final 필드로만 구성된 인터페이스를 말한다. 이 상수들을 사용하려는 클래스에서는 정규화된 이름을 쓰는 걸 피하고자 그 인터페이스를 구현하곤 한다. public interface Test { public static final String TEST_1 = "테스트1"; public static final String TEST_2 = "테스트2"; } 상수 인터페이스 안티패턴으로 인터페이..
21. 인터페이스는 구현하는 쪽을 고려해 설계해라( default 메서드)
자바 8 전에는 기존 구현체를 깨뜨리지 않고 인터페이스에 메서드를 추가할 방법은 존재하지 않았다. 자바 8부터 디폴트 메서드를 통해서 기존 인터페이스에 메서드를 추가할 수 있게 되었다. 디폴트 메서드를 선언하면, 그 인터페이스를 구현한 후 디폴트 메서드를 재정의하지 않은 모든 클래스에서 디폴트 구현이 쓰이게 된다. 단, 이렇게 디폴트 메서드를 추가한다고 해도 기존 구현체들과 매끄럽게 연동된다는 보장은 없다. 생각 할 수 있는 모든 상황에서 불변식을 해치지 않는 디폴트 메서드를 작성하기란 어렵기 때문이다. default boolean removeIf(Predicate
20. 추상 클래스보다는 인터페이스를 우선하라(템플릿 메서드 패턴)
자바가 제공하는 다중 구현 메커니즘은 인터페이스와 추상 클래스 두가지다. 둘의 가장 큰 차이는 추상 클래스가 정의한 타입을 구현하는 클래스는 반드시 추상 클래스의 하위 클래스가 되어야 한다는 점이다. 자바는 단일 상속만 지원하니, 추상 클래스 방식은 새로운 타입을 정의하는데 커다란 제약을 안게 되는 셈이다. 반면 인터페이스가 선언한 메서드를 모두 정의하고 그 일반 규약을 잘 지킨 클래스라면 다른 어떤 클래스를 상속했든 같은 타입으로 취급받는다. 기존 클래스에도 손쉽게 새로운 인터페이슬 룩현해넣을 수 있다. 인터페이스가 요구하는 메서드를 추가하고, 클래스 선언에 implements 구문만 추가하면 끝이다. 반면 기존 클래스 위에 새로운 추상 클래스를 끼워넣기는 어려운게 일반적이다. 두 클래스가 같은 추상 ..
19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라
상속을 고려한 설계와 문서화란? 우선, 메서드를 재정의하면 어떤 일이 일어나는지를 정확히 정리하여 문서로 남겨야 한다. 달리 말하면, 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 한다. 클래스의 API로 공개된 메서드에서 클래스 자신의 또 다른 메서드를 호출할 수도 있다. 그런데 마침 호출되는 메서드가 재정의 가능 메서드라면 그 사실을 호출하는 메서드의 API 설명에 적시해야 한다. 덧붙여서 어떤 순서로 담아야 호출하는지, 각각의 호출 결과가 이어지는 처리에 어떤 영향을 주는지도 담아야 한다. 더 넓게 말하면, 재정의 가능 메서드를 호출할 수 있는 모든 상황을 문서로 남겨야 한다. 예를 들어 백그라운드 스레드나 정적 초기화 과정에서도 호출이 일어날 수 있다. A..
18. 상속보다는 컴포지션을 사용하라
상속은 코드를 재사용하는 강력한 수단이지만, 항상 최선은 아니다. 잘못 사용하면 오류를 내기 쉬운 소프트웨어를 만들게 된다. 상위 클래스와 하위 클래스를 모두 같은 프로그래머가 통제하는 패키지 안에서라면 상속도 안전한 방법이다. 확장할 목적으로 설계되었고 문서화도 잘 된 클래스도 마찬가지로 안전하다. 하지만 일반적인 구체 클래스를 패키지 경계를 넘어, 다른 패키지의 구체 클래스를 상속하는 것은 위험하다. 메서드 호출과 달리 상속은 캡슐화를 깨드린다. 상위 클래스가 어떻게 구현되느냐에 따라 하위 클래스의 동작에 이상이 생길 수 있다. 상위 클래스의 릴리스에 따라 내부 구현이 달라지게 되면 그 여파로 하위 클래스가 오작동 할 수 있다는 것이다. 상위 클래스 설계자가 확장은 충분히 고려하고 문서화를 제대로 해..
17. 변경 가능성을 최소화하라
불변 클래스란 간단히 말해 그 인스턴스의 내부 값을 수정할 수 없는 클래스다. 불변 인스턴스에 간직된 정보는 고정되어 객체가 파괴되는 순간까지 절대 달라지지 않는다. 자바 플랫폼 라이브러리에도 다양한 불변 클래스가 있다. String, 기본 타입의 박싱된 클래스들, BigInteger와 BigDecimal 등이 여기 속한다. 클래스를 불변으로 만들려면 다음 다섯 가지 규칙을 따르면 된다. 객체의 상태를 변경하는 메서드를 제공하지 않는다. 클래스를 확장할 수 없도록 한다. 하위 클래스에서 부주의하게 혹은 나쁜 의도로 객체의 상태를 변하게 하는 것을 막는다. 모든 필드를 final로 선언한다. 시스템이 강제하는 수단을 이용해 설계자의 의도를 명확히 드러내는 방법이다. 새로 생성된 인스턴스를 동기화 없이 다른..