반응형

Effective Java 46

[Effective Java] Item29. 이왕이면 제네릭 타입으로

클라이언트에서 직접 형변환해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편합니다. 이를 위해서는 제네릭 타입으로 만들어야 할 경우가 많습니다. 새로운 타입을 설계할 때 뿐만 아니라 기존 타입 중 제네릭이있어야 하는 게 있다면 제네릭 타입으로 변경하는 것이 좋습니다. 간단한 실습을 통해서 기존 코드를 제네릭 타입으로 바꾸는 것을 알아보겠습니다. 기존 코드 public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { this.elements = new Object[DEFAULT_INITIAL_CAPACITY];..

[Effective Java] Item27. 비검사 경고를 제거

제네릭을 사용하면 비검사 형변환 경고, 비검사 메서드 호출 경고, 비검사 매개변수화 가변인수 타입 경고, 비검사 변환 경고 등 수 많은 컴파일 경고를 볼 수 있습니다. 이 중 제거하기 쉬운 경고도 있지만 제거하기 어려운 경고도 있습니다. 하지만 할 수 있는 한 모든 비검사 경고를 제거해야 합니다. 이러한 경고를 모두 제거 했다면 타입의 안전성이 보장됩니다. 즉, 런타임에 ClassCastException이 발생할 일이 없고, 의도대로 잘 작동하리라 확신 할 수 있습니다. 경고를 제거할 수는 없지만 타입이 안전하다고 확인할 수 있다면 @SuppressWarnings("unchecked")어노테이션을 달아 경고를 숨길 수 있습니다. 하지만 이를 오,남용하게 된다면 진짜 문제를 알리는 새로운 경고가 나와도 눈..

[Effective Java] Item26. 로(raw) 타입은 사용하지 말라

로 타입(raw type)을 알기전에 제네릭을 알아야 합니다. 제네릭 클래스와 인터페이스 선언에 타입 매개변수(type parameter)가 쓰이면, 이를 제네릭 클래스, 제네릭 인터페이스라 합니다. List인터페이스는 원소의 타입을 나타내는 타입 매개변수 E를 받습니다. 그래서 List인터페이스의 완전한 이름은 List입니다. 이와 같은 제네릭 클래스, 제네릭 인터페이스를 통틀어 제네릭 타입(generic type)이라 합니다. 로 타입(raw type) 제네릭 타입에서 타입 매개변수를 사용하지 않은 타입을 말합니다. List의 로 타입은 List입니다. 로 타입은 타입선언에서 제네릭 타입 정보가 전부 지워진 것 처럼 동작합니다. jdk5.0 이전의 코드와 (제네릭이 나오기 이전) 호환되도록 하기 위한..

[Effective Java] Item24. 멤버 클래스는 되도록 static

중첩 클래스(nested class) 클래스 안에 정의된 클래스로 자신을 감싼 바깥 클래스에서만 쓰여야 합니다. 만약 그 외에 쓰임새가 있다면 톱레벨 클래스로 만들어야합니다. 중첩 클래스의 종류는 정적 멤버 클래스, (비정적) 멤버 클래스, 익명 클래스, 지역 클래스로 네 가지입니다. 이 중 첫 번째 정적 멤버 클래스를 제외한 나머지는 내부 클래스(inner class)에 해당합니다. 정적 멤버 클래스 정적 멤버 클래스는 다른 클래스 안에 선언되고 바깥 클래스의 private 멤버에도 접근할 수 있다는 점만 빼면 일반 클래스와 똑같습니다. 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰입니다. 중첩 클래스의 인스턴스가 바깥 클래스의 인스턴스에 독립적일 수 있다면 정적 멤버 클래스로 ..

[Effective Java] Item23.태그달린 클래스보다는 클래스 계층구조 활용

public class Figure { enum Shape { RECTANGLE, CIRCLE } //태그 필드 final Shape shape; //Shape가 RECTANGLE일 때만 사용 double length; double width; //Shape가 CIRCLE일 때만 사용 double radius; //RECTANGLE용 생성자 public Figure(double length, double width) { this.shape = Shape.RECTANGLE; this.length = length; this.width = width; } //CIRCLE용 생성자 public Figure(Shape shape, double radius) { this.shape = Shape.CIRCLE; th..

[Effective Java] Item22. 인터페이스는 타입을 정의하는 용도로만 사용

인터페이스의 용도 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 합니다. 인터페이스는 오로지 이 용도로만 사용해야 합니다. 상수 인터페이스 안티패턴 상수 인터페이스란 메서드 없이, static final 필드로만 가득찬 인터페이스를 말합니다. 클래스 내부에서 사용하는 상수는 외부 인터페이스가 아니라 내부 구현에 해당합니다. 이는 내부 구현을 클래스의 API로 노출하는 행위입니다. 또한 클래스가 어떤 상수 인터페이스를 사용하든 사용자에게는 아무런 의미가 없습니다. 오히려 사용자에게 혼란을 주기도 하며, 클라이언트 코드가 내부 구현에 해당하는 상수들에 종속되게 됩니다. 상수 공개 특정 클래스나 인터페이스와 강하게 연관된 상수라면 그 클래스 자체에 추가 열거 타입 인스턴스와할 수 없는 유틸리티..

[Effective Java] Item21. 인터페이스는 구현하는 쪽을 생각해 설계

인터페이스에 메서드를 추가 자바 8이후 디폴트 메서드를 통해 기존 인터페이스에 메서드를 추가할 수 있게 되었습니다. 하지만 아래와 같은 이유로 디폴트 메서드의 추가는 신중해야합니다. 디폴트 메서드를 재정의하지 않은 모든 클래스에서 디폴트 구현이 쓰이게 됩니다. 생각할 수 있는 모든 상황에서 불변식을 해치지 않는 디폴트 메서드를 작성하기란 매우 어렵습니다. 디폴트 메서드는 기존 구현체에 런타임 오류를 일으킬 수 있습니다. 정리 디폴트 메서드는 인터페이스로붙터 메서드를 제거하거나 기존 메서드의 시그니처를 수정하는 용도가 아닙니다. 인터페이스를 설계할 때 세심한 주의를 기울여야 합니다.

[Effective Java] Item20. 추상 클래스보다는 인터페이스

추상클래스와 인터페이스 자바는 추상 클래스와 인터페이스로 다중 구현 메커니즘을 제공합니다. 자바 8부터 인터페이스도 디폴트 메서드를 제공할 수 있게 되어 두 메커니즘 모두 인스턴스 메서드를 구현 형태로 제공할 수 있습니다. 인터페이스를 우선적으로 고려해야 하는 이유 추상 클래스는 구현한 클래스는 반드시 하위클래스여야 합니다. 자바는 단일 상속만 지원하기 때문에 추가적으로 새로운 타입을 정의하는데 제약이 있습니다. Comparable 인터페이스는 jdk1.2버전에서 추가되었습니다. 반면 이를 구현하나 String클래스와 Integer클래스는 jdk1.0버전에서부터 있었습니다. 만약 Comparable을 추상 클래스 방식으로 지원한다면 String과 Integer의 공통 조상이어야 합니다. 이 외에도 Com..

[Effective Java] Item19. 상속을 고려해 설계하고 문서화하라

메서드를 재정의하면 어떤 일이 일어나는지를 정확히 정리하여 문서로 남겨야 합니다. 달리 말하면 재정의 할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 합니다. API문서의 메서드 설명 끝에 "Implementation Requirements"로 시작하는 절이 바로 내부 동작을 설명하는 곳입니다. 이는 메서드 주석에 @implSpec 태그를 붙여주면 자바독 도구가 생성해줍니다. @implSpec 태그는 자바8에 처음 도입되었습니다. 이 태그를 활성화하려면 명령줄 매개변수로 -tag "implSpec:a:Implementationo Requirements:"를 지정해주면 됩니다. 문서화 말고도 고려해야 할 것들이 더 있습니다. 클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅(hook)을..