열거타입을 확장하는 것은 대부분의 상황에서 좋지 않다. 그러나, 어울리는 쓰임이 최소한 하나는 있는데 연산 코드다. API가 제공하는 기본 연산 외에 사용자 확장 연산을 추가할 수 있도록 열어줘야할 때가 있다. 열거 타입 자체로는 확장을 할 수 없지만, 인터페이스와 그 인터페이스를 구현하는 열거타입을 이용하여 그 효과를 낼 수 있다. (열거 타입이 인터페이스를 구현할 수 있다는 사실 이용) public interface Operation { double apply(double x, double y); } public enum BasicOperation implements Operation { PLUS("+") { public double apply(double x, double y) { return x ..
ordinal 메서드로 인덱스를 얻는 코드가 있다. Set[] plantsByLifeCycle = new Set[Plant.LifeCycle.values().length]; for (int i = 0; i < plantsByLifeCycle.length; i++) { plantsByLifeCycle[i] = new HashSet(); } for (Plant p : garden) { plantsByLifeCycle[p.lifeCycle.ordinal()].add(p); } // 인덱스의 의미를 알 수 없어 직접 레이블을 달아 데이터 확인 작업 필요 for (int i = 0; i < plantsByLifeCycle.length; i++) { System.out.printf("%s: %s%n", Plant...
대부분의 열거 타입 상수는 자연스럽게 하나의 정숫값에 대응됨 모든 열거 타입의 ordinal 메서드 : 해당 상수가 그 열거 타입에서 몇번쨰 위치인지 반환 public enum Number { ONE, TWO, THREE, FOUR pbulic int convertInt() { return ordinal() + 1; } } 위 코드는 유지보수에 안좋다. 상수 선언 순서를 바꾸면 converInt가 오동작하고, 이미 사용중인 정수와 값이 같은 상수는 추가할 방법이 없다. 그리고 중간에 비워놓지도 못한다. 해결책은 열거 타입 상수에 연결된 값은 ordinal 메서드로 얻지 말고 인스턴스 필드에 저장해라. puplic enum Number { ONE(1), TWO(2), THREE(3), FOUR(4) pri..
열거 타입은 고정된 상수의 집합으로, 그 외의 값은 허용하지 않는 타입이다. ex) 계절 열거 타입이 자바에 추가되기 전엔 정수 상수를 열거하는 취약한 방법 사용 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; 위와 같은 방식은 상당히 취약 타입 안전 보장 x 표현력 좋지 않음. 컴파일러가 이해하는 ..
제네릭은 단일원소 컨테이너에 흔히 쓰인다. (매개변수화할 수 있는 타입의 수가 제한됨) Map 에는 String 타입의 값만 담을 수 있다. 그런데, 다른 타입들도 함께 담고 싶을 때는 어떻게 할까? → 타입 안전 이종 컨테이너 (서로 다른 타입을 하나의 컨테이너에 안전하게 보관) 타입 안전 이종 컨테이너 각 타입의 Class 객체를 매개변수화한 키 역할로 사용하면 된다.(Class가 제네릭이므로 가능) private Map
가변인수와 제네릭 가변인수 메서드와 제네릭은 자바 5때 함께 추가되었지만 잘 어우러지지 않는다. 가변 인수 메서드를 호출하면 가변인수를 담기 위한 배열이 자동으로 하나 만들어진다. 이를 내부로 감춰야했는데 클라이언트에 노출해버려서 제네릭이나 매개변수화 타입이 포함되면 컴파일 경고 발생 실체화 불가 타입(제네릭, 매개변수화 타입)은 런타임에는 컴파일타임보다 타입 관련 정보를 적게 가지고 있음 제네릭과 가변인수를 혼용하면 타입 안전성이 깨진다. //제네릭 varargs 배열 매개변수에 값을 저장하는 것은 안전하지 않다. public void print(List ... stringList){ List intList = List.of(42); Object[] objects = stringList; objects..
매개변수화 타입은 불공변 → List은 List의 하위타입도 상위타입도 아니다. 때로는 불공변 방식보다 유연한 무언가가 필요함 public class Stack { public Stack(); public void push(E e); public E pop(); public boolean isEmpty(); } //일련의 원소를 스택에 넣는 메서드 pushAll //와일드카드 타입을 사용하지 않음 -> 결함 public void pushAll(Iterable src) { for(E e : src) push(e); } 위 메서드는 결함이 있다. src의 원소타입이 스택의 원소 타입과 일치하면 잘 작동하지만, 만약 Stack 원소에 Integer 타입의 src를 넣으면 어떻게 될까? Integer는 Numb..
제네릭 메서드 메서드도 제네릭으로 만들 수 있다. 매개변수화 타입을 받는 정적 유틸리티 메서드는 보통 제네릭 메서드이다. ex) Collections의 binarySearch, sort 메서드 //메서드 선언에서 입력 2개와 반환 1개의 원소 타입을 타입 매개변수로 명시 //선언에서 타입 매개변수 목록은 메서드의 제한자와 반환 타입 사이에 명시 //메서드 안에서도 이 타입 매개변수만 사용 public static set union(Set s1, Set s2) { Set result = new HashSet(s1); result.addAll(s2); return result; } 불변 객체를 여러 타입으로 활용해야할 때 제네릭은 런타임에 타입 정보가 소거되기 때문에 하나의 객체를 어떤 타입으로든 매개변수화..
//Object 기반 스택 public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if(size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] ..