지식조각모음
12. 제네릭스, 열거형, 애너테이션 본문
Generics
의미
- 데이터의 타입(data type)을 일반화한다(generalize)는 것을 의미
- 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법
- 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능
간단히 말해서, 어떤 타입을 사용할지 미리 지정하는 것이다.
왜 사용할까?
제네릭을 통해 컴파일 시 타입 체크를 미리 수행하면 다음과 같은 장점이 있다.
- 객체의 타입 안정성을 높인다.
- 컴파일 타임에 객체의 타입을 체크한다. 이때 에러가 발생하면 runtime 에러보다 안전하게 에러를 체크할 수 있다.
- 형변환의 번거로움이 줄어든다.
- 반환값에 대한 현변환 및 타입 검사에 들어가는 노력을 줄일 수 있다.
- 예를 들어 반환값이 Object로 되어 있으면 intger로 변환할 때 많은 부분을 고려해야 한다. 하지만 특정 타입으로 지정되어 있으면 이런 번거로움을 줄일 수 있다.
- 코드의 재사용성을 높일 수 있다.
- 타입만 변경하면 동일한 코드를 매번 작성할 필요없다.
예 - generic type을 사용하지 않는 경우
public class Box {
private Object object;
public void set(Object object) { this.object = object; }
public Object get() { return object; }
}위와 같은 Box 클래스가 있다고 해보자. 이 클래스는 어떤 타입이든 받아서 사용할 수 잇으며 컴파일 시점에는 이 클래스가 어떻게 사용될 지 알 수 없다. 실제로 사용할 때 어떤 타입을 지정할 지, 어떤 타입을 받게 될지 모른다. get메소드를 호출하는 경우 Integer 타입을 받아야 하지만 실수로 String타입을 받도록 코드를 짤 수도 있다. 이런 경우 runtime 에러가 발생하게 된다.
Box box = new Box();
box.set(1);
// Pass
int boxResult = box.get();
// runtime error 발생
String boxResult = box.get(); 또는 매번 타입 검사를 해야한다. 예를 들어 ArrayList를 생성할 때 타입을 지정하지 않는다면 아래 예제처럼 매번 형변환(cast)를 해야한다.
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);generic type
용어
class Box<T> {}Box<T>: 제네릭 클래스. 'T Box'라고 읽는다- Box: 원시 타입(raw type)
- T: 타입 변수 또는 타입 매개변수
타입 변수에 타입을 지정하는 것을 '제네릭 타입 호출'이라고 하고, 타입 변수 대신 지정된 타입을 '대입된 타입(parameterized type)'이라고 한다.
타입변수
클래스 이름 옆의 '<>'안에 있는 E를 '타입 변수(type variable)'라고 하며, 일반적으로는 'Type'의 첫 글자를 따서 T를 사용한다. generic 클래스는 다음과 같은 포맷을 따른다. 참고로 <>은 영어로 angle brackets이다.
class name<T1, T2, ..., Tn> { /* ... */ }이런 타입변수는 아래와 같은 naming 컨벤션을 따른다. (참고)
- E - Element (used extensively by the Java Collections Framework)
- K - Key
- N - Number
- T - Type
- V - Value
- S,U,V etc. - 2nd, 3rd, 4th types
또한 타입 변수를 여러개로 지정할 수 있다.
public interface Pair<K, V> {
public K getKey();
public V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
// example
Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String> p2 = new OrderedPair<String, String>("hello", "world");심지어는 타입 변수에 타입 변수를 사용할 수도 있다. 예를 들어 Pair 클래스를 선언할 때 타입 변수에 Box<Integer>을 사용하여 다음과 같이 사용할 수 있다.
OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));예 - generic type을 사용하는 경우
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
public interface List <E> {
void add(E x);
Iterator<E> iterator();
}
public interface Iterator<E> {
E next();
boolean hasNext();
}
generic 제한
타입 문자로 사용할 타입을 명시하면 한 종류의 타입만 저장할 수 있다. 하지만 여전히 모든 종류의 타입을 지정할 수 있다.
FruitBox<Toy> fruitBox = new FruitBox<Toy>(); // 과일 상자에 장난감을 담을 수 있다.
특정 타입만 사용할 수 있도록 하려면 extends를 사용해야 한다. 이러면 특정 타입의 자손들만 대입할 수 있게 제한할 수 있다.
class FruitBox<T extends Fruit> { ... } // Fruit의 자손만 타입으로 지정 가능
FruitBox<Toy> fruitBox = new FruitBox<Toy>(); // 에러 발생
Enum
상수들의 집합
예: 일반적인 상수 선언 방식
Class Card { static final int CLOVER = 0; static final int HEART = 1; static final int DIAMOND = 2; static final int SPADE = 3; static final int TWO = 0; static final int THREE = 1; static final int FOUR = 2; final int kind; final int num; }위와 같이 선언한 것을 열거형을 이용하여 변경할 수 있다.
class Card { enum Kind {CLOVER, HEART, DIAMOND, SPADE} enum Value {TWO, THREE, FOUR} final Kind kind; final Value value; }
'책 > 자바의 정석' 카테고리의 다른 글
| 14. 람다와 스트림 (2) | 2022.04.09 |
|---|---|
| 13. 스레드 (2) | 2022.03.30 |
| Comparator와 Comparable (1) | 2022.03.10 |
| 11. 컬렉션 프레임웍 (0) | 2022.03.06 |
| 10. 날짜와 시간 & 형식화 (1) | 2022.03.05 |