제네릭스(Generics)
- 컴파일시 타입을 체크해 주는 기능(compile-time type check) - JDK1.5
- 객체의 타입 안정성을 높이고 형변환의 번거러움을 줄여줌
(하나의 컬렉션에는 대부분 한 종류의 객체만 저장)
제네릭스 클래스의 선언
- 클래스를 작성할 때, Object타입 대신 타입 변수 E를 선언해서 사용
public class ArrayList extends AbstractList {
transient Object[] elementData;
public boolean add(Object o) { /* 구현 생략 */ }
publcc Object get(int index) { /* 구현 생략 */ }
}
public class ArrayList<E> extends AbstractList<E> {
transient E[] elementData;
public boolean add(E o) { /* 구현 생략 */ }
public E get(int index) { /* 구현 생랴 */ }
}
- 타입변수 대신 실제 타입을 지정하면 형변환 생략가능
ArrayList list = new ArrayList();
list.add(new Point());
Point p = (Point)list.get(0);
//==//
ArrayList<Point> list = new ArrayList<Point>();
list.add(new Point());
Point p = list.get(0);
제네릭스의 용어
- BOX<T> : 제네릭 클래스. 'T의 Box' 또는 'T Box'라고 읽는다.
- T : 타입 변수 또는 타입 매개변수. (T는 타입 문자)
- Box 원시 타입(raw type)

제네릭스의 제한
- static 멤버에는 타입 변수를 사용할 수 없음
class Box<T> {
static T item; // 에러
static int compare(T t1, T t2) { .. } // 에러
}
- T는 어떤 값이 될지 모르지만 static변수는 모든 클래스에 공통이여야 하기 때문에 사용할 수 없다.
- 배열을 생성할 때 타입 변수를 사용할 수 없지만 타입 변수로 배열 선언은 가능
class Box<T> {
T[] itemArr; // OK. T타입의 배열을 위한 참조변수
t[] toArray() {
T[] tmpArr = new T[itemArr.length]; // 에러. 제네릭 배열 생성 불가
return tmArr;
}
}
- 객체나 배열을 사용하기 위해선 확정이 되어있어야 한다.
- new 뒤에 사용불가
제네릭 클래스의 객체 생성과 사용
- 제네릭 클래스 Box<T>의 선언
class Box<T> {
ArrayList<T> list = new ArrayList<t>();
void add(T item) { list.add(item); }
T get(int i) { return list.get(i); }
ArrayList<T> getList() { return list; }
}
- Box<T>의 객체 생성. 참조변수와 생성자에 대입된 타입이 일치해야 함
Box<Apple> appleBox = new Box<Apple>(); // OK.
Box<Apple> appleBox = new Box<Grape> () // 에러 대입된 타입이 다르다
- 두 제네릭 클래스가 상속관계이고, 대입된 타입이 일치하는 것은 가능하다.
Box<Apple> appleBox = new FruitBox<Apple>(); // OK. 다형성 적용
- 대입된 타입과 다른 타입의 객체는 추가할 수 없다.
Box<Apple> appleBox = new Box<Apple>();
appleBox.add(new Apple()); // OK.
appleBox.add(new Grape()); // 에러. Box<Apple>에는 Apple객체만 추가가능
- 제네릭 타입에 'extends'를 사용하면, 특정 타입의 자손들만 대입할 수 있게 제한할 수 있음
class FruitBox<T extends Fruit> {
ArrayList<T> list = new ArrayList<T>();
void add(T item) { list.add(item); }
}
- add()의 매개변수의 타입 T도 Fruit와 그 자손 타입이 될 수 있음
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
fruitBox.add(new Apple()); // OK. Apple이 Fruit의 자손
- 인터페이스의 경우에도 'implements'가 아닌, 'extends'를 사용
interface Eatable{}
class Fruit<T extends Eatable> { ... }
와일드 카드 '?'
- 제네릭 타입에 와일드 카들르 쓰면, 여러 타입을 대입가능
단, 와일드 카드에는 <? extends T & E>와 같이 '&'를 사용불가
<? extends T> 와일드 카드의 상한 제한. T와 그 자손들만 가능
<? super T> 와일드 카드의 하한 제한. T와 그 조상들만 가능
<?> 제한 없음. 모든 타입이 가능. <? extends Object>와 동일
제네릭 메서드
- 반환타입 앞에 제네릭 타입이 선언된 메서드
- 클래스 타입 매개변수 <T>와 메서드의 타입 매개변수 <T>는 별개
- 제네릭 메서드를 호출할 때, 타입 변수에 타입을 대입해야 한다. (대부분의 경우 추정이 가능하므로 생략할 수 있음)
제네릭 타입의 형변환
- 제네릭 타입과 원시 타입간의 형변환은 바람직하지 않음
- 와일드 카드가 사용된 제네릭 타입으로 형변환 가능
열거형
- 관련된 상수들을 같이 묶어 놓은 것
- JAVA는 타입에 안전한 열거형을 제공
Class Menu {
static int ESPRESSO = 0;
static int AMERICANO = 1;
static int LATTE = 2;
}
class Menu2 {
enum CoffeeMenu { ESSPRESSO, AMERICANO, LATTE}
CoffeeMenu coffeeMenu;
}
열거형의 정의와 사용
- 열거형을 정의하는 방법
enum 열거형이름 { 상수명1, 상수명2, ... }
- 열거형 타입의 변수를 선언하고 사용하는 방법
enum Direction { EAST, SOUTH, WEST, NORTH }
class Unit {
int x, y;
Direction dir;
void init() {
dir = Direction.EAST;
}
}
- 열거형 상수의 비교에 == 와 compareTo() 사용가능
열거형의 조상 - java.lang.Enum
- 모든 열거형은 Enum의 자손이며, 아래의 메서드를 상속받는다.

- 컴파일러가 자동적으로 추가해주는 values(), valueOf()가 있음
열거형에 멤버 추가하기
- 불연속적인 열거형 상수의 경우, 원하는 값을 괄호()안에 적는다.
- 괄호()를 사용하려면, 인스턴스 변수와 생성자를 새로 추가해 줘야 한다.
- 열거형의 생성자는 묵시적으로 private이므로, 외부에서 객체생성 불가
enum Direction {
EAST(1), SOUTH(5), WEST(-1), NORTH(10); // 끝에 ';'를 추가해야 한다.
private final int value; // 정수를 저장할 필드 (인스턴스 변수)를 추가
Direction(int value) { this.value = value; } // 생성자를 추가
public int getValue() { return value; }
}
'Java' 카테고리의 다른 글
| 불편해서 직접만든 CapsLock 표시 (1) | 2023.12.23 |
|---|---|
| 쓰레드 (0) | 2022.12.06 |
| Java기초 예외처리 (0) | 2022.12.04 |