Java에서 열거 타입을 지원하기 전에는 정수 상수를 한 묶음 선언해서 사용하는 정수 열거 패턴을 사용했다. 하지만 정수 열거 패턴에는 많은 단점이 존재한다. 정수 열거 패턴의 단점 다음과 같은 정수 열거 패턴이 있다. public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_GRANNY_SMITH = 2; public static final int ORANGE_NAVEL = 0; public static final int ORANGE_TEMPLE = 1; public static final int ORANGE_BLOOD = 2; 이 패턴의 단점은 무엇일까? 1...
Java가 제공하는 주요 함수형 인터페이스 java.util.function은 Java에서 자주 사용되는 함수형 인터페이스를 미리 정의해둔 패키지이다. 대표적으로 다음과 같은 함수형 인터페이스가 존재한다. Function BiFunction Consumer Supplier Predicate UnaryOperator BinaryOperator 각 함수형 인터페이스에 대해 알아보자. Function T 타입을 받아서 R 타입을 리턴하는 함수형 인터페이스이다. Integer 타입을 받아서 Integer 타입을 리턴하는 간단한 예제를 살펴보자. public static void main(String[] args) { Function plus10 = (i) -> i + 10; System.out.println(p..
타입 안전 이종 컨테이너 패턴 컨테이너 대신 키를 매개변수화하고 컨테이너에 값을 넣거나 뺄 때 매개변수화한 키를 함께 제공하는 것을 타입 안전 이종 컨테이너 패턴이라고 한다. 하나의 컨테이너에서 매개변수화되는 대상은 컨테이너 자신이다. 따라서 하나의 컨테이너에서 매개변수화할 수 있는 타입의 수가 제한된다. 이보다 유연한 수단이 필요할 때 타입 안전 이종 컨테이너 패턴을 사용할 수 있다. 이제 타입 안전 이종 컨테이너 패턴의 예제를 살펴보자. // 타입 안전 이종 컨테이너 패턴 - API public class Favorites{ public void putFavorite(Class type, T instance); public T getFavorite(Class type) } 각 타입의 Class 객체를..
제네릭과 가변 인수를 함께 쓸 때 문제점 가변 인수 메서드(varargs)는 제네릭과 함께 Java5에 함께 추가되었다. 함께 추가되었으니 서로 잘 어우러질 거라 기대하지만 사실을 그렇지 않다. 가변 인수 메서드를 호출하면 자동으로 배열이 만들어진다. 내부로 감췄어야 할 배열을 클라이언트로 노출하게 되면서 문제가 발생한다. 제네릭이나 매개변수화 타입을 포함한 가변 인수 메서드를 호출하면 컴파일러가 경고를 보낸다. 아래의 예시처럼 힙 오염이 일어날 수 있기 때문이다. // 제네릭과 가변인수를 혼용하면 타입 안전성이 깨진다. static void dangerous(List...stringLists){ List intList = List.of(42); Object[] objects = stringLists; ..
함수형 인터페이스(Functional Interface) 함수형 인터페이스는 추상 메서드를 하나만 가진 인터페이스를 말한다. // 함수형 인터페이스! public interface DoSomething { void doAnything(); } Java 8에 새롭게 추가된 default 메서드나 static 메서드가 함께 존재한다고 하더라도 추상 메서드가 하나라면 함수형 인터페이스이다. // 함수형 인터페이스! public interface DoSomething { void doAnything(); default void printDefault() { System.out.println("default"); } static void printStatic() { System.out.println("static..
Item28- 배열보다는 리스트를 사용하라에서도 다뤘듯 매개변수화 타입은 불공변이다. List에는 어떤 객체든 넣을 수 있지만 Lisit은 String만 넣을 수 있다. 즉, List은 List가 하는 일을 제대로 수행하지 못하니 리스코프 치환 원칙에 어긋나고, 따라서 하위 타입이라 볼 수 없다. 이러한 불공변성 때문에 매개변수화 타입은 유연함이 부족하다. 따라서 불공변의 유연함을 극복하려면 한정적 와일드카드 타입을 사용해야 한다. Stack 예제 E 생산자 매개변수에 와일드카드 타입 :
Item29에서 설명한 것과 마찬가지로, 클라이언트에서 입력 매개변수와 반환 값을 명시적으로 형 변환하는 메서드보다 제네릭 메서드가 더 안전하고 사용하기도 쉽다. 이왕이면 제네릭 타입으로 만든 것처럼 메서드도 제네릭 메서드로 만들자. 매개변수화 타입을 받는 정적 유틸리티 메서드는 보통 제네릭이다. 예를들면 Collections의 알고리즘 메서드(sort, binarySearch)는 모두 제네릭이다. 제네릭 메서드 작성법 단순한 제네릭 메서드 public static Set union(Set s1, Set s2) { Set result = new HashSet(); result.addAll(s2); return result; } 타입 매개변수 목록은 메서드의 제한자와 반환 타입 사이에 온다. 위와 같은 제..
클라이언트가 직접 형 변환해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다. 따라서 새로운 타입을 설계할 때는 형 변환 없이도 사용할 수 있도록 제네릭 타입으로 만들자. 예시 Object 기반 스택을 제네릭 타입으로 변환 public class Stack { private E[] elements; private int size; public Stack() { this.elements = new E[16]; } public void push(E e) { elements[size++] = obj; } public E pop() { if (size == 0) { throw new EmptyStackException(); } E result = elements[--size]; elements[size]..
배열과 제네릭 타입의 가장 큰 차이는 공변과 실체화다. 배열은 공변이며 실체화가 가능하고 제네릭은 불공변이며 실체화가 불가능하다. 배열보다 리스트를 사용해야 하는 첫 번째 이유 개발자라면 누구나 컴파일 타임에 오류를 발견하는 것을 선호한다. 배열은 런타임에야 오류를 발견할 수 있지만, 제네릭은 컴파일 타임에 오류를 발견할 수 있다. 아래의 설명을 통해 왜 그런지 알아보자. 배열은 공변? 공변은 함께 변한다는 뜻이다. Item28에서 말하는 배열의 공변이란 Sub가 Super의 하위 타입이라면 배열 Sub[]는 Super[]의 하위 타입이라는 것을 의미한다. 더 쉽게 이해하기 위해 다음 코드를 살펴보자. // 컴파일에 이상이 없다. Object[] objectArr = new Long[1]; // 공변 L..