본문 바로가기

Java

제네릭스

제네릭스(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