Book/Effective Java

    [Item 34] int 상수 대신 열거 타입을 사용하라

    자바에서 열거 타입을을 제공하기 전에 정수 상수를 한 묶을 선언해서 사용했다. public static final int A = 1; public static final int B = 1; public static final int C = 1; 위와 같은 열거 패턴의 단점 타입 안전을 보장하는 방법이 없으며 표현력도 좋지 않다. A 라는 상수를 사용해야하는 메서드에 B 라는 상수를 사용해도 컴파일러는 해줄 수 있는게 없다. 동등성을 검증할 때도 경고 메시지를 출력하지 않는다. 상수의 값이 바뀌면 클라이언트도 반드시 다시 컴파일 해야한다. 상수의 갯수를 파악하기 어렵다. 문자열을 상수로 만들 경우 하드 코딩하게 만들기 때문에 안정적이지 못하다. 단점을 보완하기 위해 열거타입이 제공된다. public enu..

    [Item 33] 타입 안전 이종 컨테이너를 고려하라

    class 리터럴의 타입은 Class가 아닌 Class 다. String.class 는 Class Integer.class 는 Class 컴파일타임 타입 정보와 런타임 타입 정보를 알아내기 위해 메서드들이 주고 받는 class 리터럴을 타입 토큰(type token) 이라 한다. 타입 안전 이종 컨테이너 패턴(type safe heterogeneous container pattern) 컨테이너 대신 키를 매개변수화한 다음, 컨테이너에 값을 넣거나 뺄 때 매개변수화한 키를 함계 제공 하는 패턴 이종 컨테이너 패턴 예제 코드 public class Favorites { public void putFavorites(Class type, T instance); public T getFavorites(Class ..

    [Item 32] 제네릭과 가변인수를 함께 쓸 때는 신중하라

    [Item 32] 제네릭과 가변인수를 함께 쓸 때는 신중하라

    가변인수 메서드를 호출하면 가변인수를 담기 위해 배열이 자동으로 하나 만들어진다. 내부에서 사용하는 배열이 클라이언트에게 노출되는 문제점이 있다. 제네릭이나 매개변수화 타입을 포함한 가변 인수 메서드를 호출하면 컴파일러가 경고를 보낼것이다. 매개 변수화된 Vararg 유형으로 인한 힙 오염 가능성으로 경고가 발생한다고 확인 할 수 있다. 제네릭과 가변인수를 같이 사용하면 타입 안정성이 깨진다. public static void test1(List...stringLists){ List intList = List.of(42); Object[] objects = stringLists; objects[0] = intList; // 힙오염 발생 String s = stringLists[0].get(0); // C..

    [Item 31] 한정적 와일드카드를 사용해 API 유연성을 높이라

    제네릭 타입 즉, 매개변수화 타입은 불공변(invariant)이다. 아이템 28에서 살펴봤을 것이다. 하지만 불공변 방식보다 유연한 무언가가 필요하다. 아래와 같은 스택 구조가 있다고 가정해보자. public class Stack { private Object[] objects; private int index; private int top = -1; public Stack(int size) { this.objects = new Object[size]; } public void push(E e) { this.objects[index] = e; index++; } @SuppressWarnings("unchecked") public E pop() { E element = (E) this.objects[ind..

    [Item 30] 이왕이면 제네릭 메서드로 만들라

    클래스를 제네릭 클래스를 만들수 있고 메서드도 제네릭 메서드로 만들수 있다. 메개변수화 타입을 받는 정적 유틸리티 메서드는 보통 제네릭이다. 예를 들어 Collections 클래스의 sort를 보면 제네릭으로 구성 되어있다. 제네릭 메서드 작성법 로타입 사용 - 수용 불가 → 경고 발생 public static Set union(Set s1, Set s2) { Set result = new HashSet(s1); result.addAll(s2); return result; } 경고를 없애려면 타입 안전하게 만들어야 한다. 타입 매개변수 목록은 메서드의 제한자와 반환 타입 사이에 온다. 제네릭 메서드로 수정 public static Set union(Set s1, Set s2) { Set result = ..

    [Item 29] 이왕이면 제네릭 타입으로 만들라

    책에는 Stack 클래스를 예로 들었는데 이 글은 stack클래스를 모방한 Cage 클래스로 예를 들겠습니다. public class Cage { private Object[] cages; private int size; private static intDEFAULT_SIZE= 10; public Cage() { this.cages = new Object[DEFAULT_SIZE]; } public void push(Object o) { checkCapacity(); this.cages[size++] = o; } public Object pop() { if (size == 0) { throw new EmptyStackException(); } Object result = cages[--size]; cage..

    [Item 28] 배열보다는 리스트를 사용하라

    배열과 제네릭 타입의 차이 1. 공변과 불공변 배열은 공변이다 sub가 super의 하위 타입이라면 sub[]는 배열 super[]의 하위 타입이 된다. Object[] objectArray = new Long[1]; objectArray[0] = "어떤 타입이든 저장할 수 있는데.........."; // ArrayStoreException 던짐 배열은 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다. 그래서 컴파일에는 아무런 영향을 끼치지 않지만 런타임 시 ArrayStoreException을 던진다. 제네릭은 불공변이다. 서로 다른 타입 type1과 type2가 있을 때 List 은 List 의 상위 타입도 아니고 하위 타입도 아니다. // 컴파일 시 예외 List list = new ..

    [Item 27] 비검사 경고를 제거하라

    제네릭을 사용하기 시작하면 수많은 컴파일러 경고들을 마주치게 되는데 이러한 경고들은 가능한 많이 제거하는 것이 좋다. 경고들을 모두 제거 한다면, 그 코드는 타입의 안정성이 보장된다. 비검사 경고는 쉽게 제거할 수 있다. List list = new ArrayList(); 컴파일 경고가 나오는 코드다. List list = new ArrayList(); 제네릭 타입으로 인스턴스를 생성하고 뒤에 다이아몬드 연산자를 생성해야 한다. 경고를 제거할 수 없지만 타입이 안전하다고 보장되면 경고를 숨겨라 @SuppressWarnings("unchecked")으로 경고를 숨기자. 단, 타입 안정성을 검증하지 않고 경고를 숨기면 절대 안된다. 해당 코드는 경고 없이 컴파일되지만, 런타임 시 여전히 ClassCastEx..

    [Item 26] 로 타입(Raw Type)은 사용하지 말라

    로 타입은 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말한다. List list = new ArrayList(); 위와 같은 형태를 말한다. 로 타입의 문제점 타입 안정성과 표현력을 확보할 수 없다. 문제점이 컴파일 시에 걸러내지 못하고 런타임에 예외가 발생한다. 따라서 매개 타입을 선언해서 타입 안정성과 표현력을 가져가야 한다. List와 List의 차이점 임의의 객체 타입을 받는다는 표현을 한 것이고 두 동작은 같을 것이다. 대신에 컴파일러가 이 사실을 아냐 모르냐에 차이점이 있다. Object 의 매개 타입은 모든 타입을 허용한다는 표현을 한것이고 컴파일러에게 전달한다. 이로 인해 List은 하위 타입 규칙과 타입 안정성을 모두 지킬 수 있게 된다. 매개 변수 타입에서 사용된다면 pu..

    [Item 25] 톱레벨 클래스는 한 파일에 하나만 담으라

    소스를 작성하는 파일 내에 하나만 존재하는 클래스를 톱레벨 클래스라고 한다. 소스 파일 하난에 여러개의 톱레벨 클래스가 있다면 문제가 있다. 문제 Product.java class Product { static final StringNAME= "phone"; } class Company { static final StringNAME= "samsung"; } Company.java public class Company { static final StringNAME= "samsung"; } class Product { static final StringNAME= "phone"; } 위의 소스코드는 컴파일 에러에서 중복된 클래스라 에러가 발생할 것이다. 컴파일러에게 어느 소스파일을 먼저 읽게 하냐 에 따라 정..

    [Item 24] 멤버 클래스는 되도록 static 으로 만들라

    중첩 클래스(nested class)란 다른 클래스 안에 정의된 클래스를 말한다. 자신을 감싼 바깥 클래스에서만 쓰여야 한다. 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다. 중첩 클래스의 종류 정적 멤버 클래스 (비정적) 멤버 클래스 익명 클래스 지역 클래스 정적 멤버 클래스를 제외한 나머지 3가지가 내부 클래스(inner class)에 해당한다. 정적 멤버 클래스와 비정적 멤버 클래스는 구문상 차이는 단지 static이 붙어 있고의 차이뿐이지만, 의미상 차이는 꽤 크다. 비정적 멤버 클래스 인스턴스 생성 public class Outer { private int a; class inner { private int b; } } Outer outer = new Outer(); Outer.inne..

    [Item 23] 태그 달린 클래스보다 클래스 계층구조를 활용하라

    태그 달린 클래스는 장황하고, 오류를 내기 쉽고, 비효율적이다. 클래스 계층구조보다 나쁘다. public class Figure { enum Shape {RECTANGLE,CIRCLE} private final Shape shape; private int length; private int width; private int radius; public Figure(int radius) { this.shape = Shape.CIRCLE; this.radius = radius; } public Figure(int length, int width) { this.shape = Shape.RECTANGLE; this.length = length; this.width = width; } public int area(..

    [Item 22] 인터페이스는 타입을 정의하는 용도로만 사용하라

    인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다. 달리 말해, 클래스가 어떤 인터페이스를 구현한다는 것은 자신의 인스턴스로 무엇을 할 수 있는지를 클라이언트에 얘기해주는 것이다. 인터페이스는 오직 이 용도로만 사용해야 한다. 상수 인터페이스 안티패턴 public interface PhysicalConstants { static final intFIRST= 1; static final intSECOND= 2; static final intTHREE= 3; } 이러한 인터페이스는 안티 패턴이므로 사용하지 말자. 클래스 내부에서 사용하는 상수는 외부 인터페이스가 아니라 내부 구현에 해당한다. 클라이언트 코드가 내부 구현에 해당하는 이 상수들에 종속되게 한다. 상수를 공개하는 방..

    [Item 21] 인터페이스는 구현하는 쪽을 생각해 설계해라

    자바 8 전에는 기존 구현체를 깨뜨리지 않고는 인터페이스에 메서드를 추가할 방법이 없었다. 인터페이스에 메서드를 추가하면 기존 구현체에 이미 메서드가 존재하는 가능성이 적기에 자바 8 이후에 기존에 인터페이스에 메서드를 추가할 수 있도록 디폴트 메서드를 제공했다. 디폴트 메서드는 주로 람다를 활용하기 위해 선언하는데, 디폴트 메서드를 선언하면, 그 인터페이스를 구현한 후 디폴트 메서드를 정의 하지 않은 모든 클래스에서 사용이 가능하다. 불변식을 해치지 않는 디폴트 메서드를 작성하기란 어려운 법이다. Collection 인터페이스의 removeIf() 의 디폴트 메서드를 보면 모든 Collection 구현체와 어우러지는 것은 아니다. Collection 인터페이스를 구현한 SynchronizedCollec..

    [Item 20] 추상 클래스보다는 인터페이스를 우선하라

    자바에서 제공하는 다주우 구현 메커니즘 인터페이스 추상 클래스 자바 8 이후에 인터페이스도 디폴트 메서드를 제공할 수 있게 되어 두 메커니즘 모두 인스턴스 메서드를 구현 형태로 제공할 수 있다. 추상 클래스가 정의한 타입을 구현하는 클래스는 반드시 추상 클래스의 하위 클래스가 되어야 한다는 점이다. 상속을 통해 하위 클래스가 되어야 된다라는 말인거 같다. 자바는 단일 상속만 지원 추상 클래스 방식은 새로운 타입을 정의하는 데 제약을 안게 되는 셈이다. 하위 클래스가 되어야 하는 제약 인터페이스가 선언한 메서드를 모두 정의하고 그 일반 규약을 잘 지킨 클래스라면 다른 어떤 클래스를 상속했든 같은 타입으로 취급된다. 차이점 정리 추상 클래스를 구현한 클래스는 하위 클래스가 되는 제약을 갖는다. 인터페이스는 ..