도라에몽 개발자

지네릭스(Generics) 본문

LANGUAGE/Java

지네릭스(Generics)

Doraemon_lulu 2023. 11. 29. 18:11
  • 정의
    - 컴파일 시 타입을 체크해주는 기능을 의미함. (compile-time type check)
    - 미리 저장할 객체의 타입을 지정하여 알려줌으로서, 다른 타입의 객체는 저장하지 못하도록 함.
     Ex. ClassCastException (형변환 에러)과 같은 RuntimeException의 발생 방지하여 Runtime error (실행 중 발생하는 오류) 발생하기 전, compile time error (컴파일 시 발생하는 오류) 발생시켜 미리 수정할 수 있도록 함.
     (∵ Runtime error 발생시 프로그램 죽어서 이슈가 훨씬 큰 편...)

  • 특징
    - 객체의 타입 안정성을 높이고, 형변환의 번거로움을 줄여줌.
    → 형변환 생략 가능하여 코드가 간결해지는 장점 있음.
// TV 객체만 저장할 수 있는 ArrayList 생성
ArrayList<Tv> tvList = new ArrayList<Tv>;

tvList.add(new Tv());
tvList.add(new Audio()); // 컴파일 에러 발생함. Tv 객체 외에 다른 타입은 저장 불가함.

 

 

타입 변수

  • 정의
    - 클래스를 작성할 때, Object 타입 대신에 '타입 변수(E)'를 선언하여 사용하는 것
    - '타입 변수'는 보통 Type의 T 또는 Element의 E를 사용하기도 하나, 어떤 알파벳을 쓰든 상관은 없음.

  • 타입 변수에 '대입'하는 방법
    - 객체 생성 시, 타입 변수(E) 대신 실제 타입(Tv)을 지정(=대입)해줌.
    - 타입 변수 대신 실제 타입이 지정되면, 형변환 생성 생략 가능하다는 장점이 있음. 

 

지네릭스(Generics) 용어

  • Box<T>
    - 지네릭 클래스로, 'T의 Box' 또는 'T Box'라고 읽음.
  • T
    - 타입 변수 또는 타입 매개변수라고 함. 
  • Box
    - 원시 타입 (raw type)

 

지네릭 타입과 다형성

  • 주의사항
    - 참조 변수와 생성자의 대입된 타입은 일치해야 함.
     → 조상과 자손의 관계이더라도, 참조 변수와 생성자의 대입된 타입은 일치해야 함.
    - 지네릭 클래스간의 다형성은 성립되며, 대입된 타입 자체는 일치해야 함.
    - 매개 변수의 다형성은 성립됨.
ArrayList<Product> list = new ArrayList<Product>();
ArrayList<Product> list = new ArrayList<Tv>(); // 타입 불일치 시 error 발생
List<Tv> list = new LinkedList<Tv>(); // 클래스 타입 간의 다형성 적용 가능

list.add(new Product());
// Product의 자손객체인 Tv, Audio 저장 
list.add(new Tv());    // Product의 자손인 Tv 객체 저장 
list.add(new Audio()); // Product의 자손인 Audio 객체 저장 

// 대신 ArrayList에 저장된 Tv, Audio 객체를 꺼낼 때는, 형변환 시행 필요함. 
Product p = list.get(0);      // 형변환 불필요 
Tv t = (Tv)list.get(1)        // 형변환 시행
Audio a = (Audio)list.get(2); // 형변환 시행

 

 

지네릭스(Generics) 클래스의 종류

  • Iterator<E>
    - 클래스를 작성할 때, Object 타입 대신 T와 같은 타입 변수를 사용함.

  • HashMap<K,V>
    - 여러 개의 타입 변수가 필요한 경우에는 콤마(,)를 구분자로 선언함.

 

제한된 지네릭 클래스

  • 정의
    - extends로 대입할 수 있는 타입을 제한함.
     → 특정 타입의 '자손들' 대입할 수 있도록 제한 가능함. 
    - 인터페이스인 경우에도 extends를 활용함. (cf. 원래 인터페이스는 implements를 사용함.)

    cf. 클래스 A의 자손이면서, 동시에 B 인터페이스도 함께 구현해야 하는 경우에는 '&' 기호를 사용하여 연결함.
     → class FruitBox<T extends A & B> { ... }

  • 지네릭스의 제약
    1) 타입 변수에 대입은 인스턴스 별로 다르게 가능함.
    2) static 멤버에는 타입 변수 사용 불가함.
    3) 배열 생성 시 타입 변수 사용 불가하나, 타입 변수로 배열 선언은 가능함. 
// 여러 개의 객체별 다른 타입 지정 가능
Box<Apple> appleBox = new Box<Apple>();
Box<Grape> grapeBox = new Box<Grape>();

// static 멤버에 타입 변수 T 사용 불가함.
static T item; // error

// 지네릭 배열 타입 참조변수 선언은 가능하나, 지네릭 배열 생성은 불가함.
T[] itemArr; // T타입 배열을 위한 참조변수 itemArr 선언은 가능함.
T[] temArr = new T[itemArr.length]; // error - 지네릭 배열, 객체 생성 불가능함. (new T... 불가)

 

 

 

와일드 카드

  • 정의
    - 하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능함. (*타입이 달라도 허용되도록 하는 필살기라고 생각하면 될 듯)

  • 종류
    <? extends T> 
    - 와일드 카드의 상한 제한, T와 그 자손들만 가능함.
    <? super T> 
    - 와일드 카드의 하한 제한, T와 그 조상들만 가능함. 
    <?> 
    - 제한 없이, 모든 타입에 가능함. <? extends Object>와 동일하다고 보면 됨.

 

 

지네릭 메서드

  • 정의
    - 지네릭 타입이 선언된 메서드로, 타입 변수는 메서드 내에서만 유효함.
    - 클래스의 타입 매개변수<T>와 메서드 타입 매개변수<T>는 별개임.
    - 메서드를 호출할 때마다 다른 지네릭 타입을 대입할 수 있음. (대부분 생략 가능)
     → 보통 와일드 타입 사용 불가할 때 지네릭 메서드를 사용하는 경우가 많음.
    - 메서드를 호출할 때 타입을 생략하지 않는 경우에는 클래스 이름 생략 불가함. 

 

 

지네릭 타입의 형변환

  • 정의 및 활용
    - 지네릭 타입과 원시 타입 간의 (타입 간의)형변환은 가능은 하지만, 바람직하지 않음. (*경고 발생) 
     ▶ 오류 발생을 줄이기 위해, 참조 변수의 객체 타입과 생성자의 객체 타입 모두 생략하지 않고 작성하는 것을 권장함. 
    - 와일드 카드가 사용된 지네릭 타입으로는 형변환 가능함. 
Box box = null;
Box<Object> objBox = null;

// 타입 간의 형변환 - 가능, 경고
box = (Box)objBox; // 지네릭 타입을 원시 타입으로 형변환; 경고 발생 
objBox = (Box<Object>)box; // 원시 타입을 지네릭 타입으로 형변환; 경고 발생  

Box<Object> objBox = null;
Box<String> strBox = null;

// 대입된 타입이 다른 지네릭 타입 간의 형변환 - 불가능, 에러
objBox = (Box<Object>)strBox; // Box<String>을 Box<Object>로 형변환; error
strBox = (Box<String>)objBox; // Box<Object>를 Box<String>로 형변환; error

 

 

 

지네릭 타입의 제거

  • 정의 및 활용
    - 컴파일러는 지네릭 타입을 제거하고, 필요한 곳에 형변환을 넣음.
    ① 지네릭 타입의 경계(bound)를 제거함.
    ② 지네릭 타입 제거 후에 타입이 불일치하면 형변환 추가함.
    ③ 와일드 카드가 포함된 경우, 적절한 타입으로 형변환 추가함.
/* 지네릭 타입 경계 제거 */
class Box<T extends Fruit> { // Box의 타입은 Fruit의 자손과 같다는 의미
	void add(T t) { ... }
}

// 위와 같은 코드를 아래와 같이 수정할 수 있음. 
class Box {                   // 지네릭 타입 선언(<T extends Fruit>) 제거 
	void add(Fruit t) { ... } // T는 Fruit으로 치환됨.
}

'LANGUAGE > Java' 카테고리의 다른 글

애너테이션(Annotations)  (0) 2023.11.29
열거형(enum)  (1) 2023.11.29
Stream API  (1) 2023.11.28
Collections Framework (컬렉션 프레임웍) - Collections  (0) 2023.11.28
Collections Framework (컬렉션 프레임웍) - HashMap, Hashtable  (1) 2023.11.28