프로그래밍 언어&프레임워크/java

[Java/자바] 제네릭 프로그래밍

Coblin 2021. 9. 13. 00:31
반응형

들어가기 전에

본 글은 제가 참여하고 있는 자바 기초 스터디에서 제가 발표한 내용으로 위의 책 Chapter 12 컬렉션 프레임워크 파트를 정리한 글입니다.

 

틀린 내용이나 오타가 있다면 댓글로 알려주시면 감사하겠습니다.


제네릭(Generic) 프로그래밍

  • 변수의 선언이나 메서드의 매개변수를 하나의 자료형이 아닌 여러 자료형으로 변환될 수 있도록 프로그래밍하는 방식
    ex) String[] strArray = {"strOne", "strTwo"};
  • Int[] intArray = {1, 2};
  • 데이터 형식에 의존하지 않고 하나의 값이 여러 다른 데이터 타입들을 가질 수 있는 기술에 중점을 두어 재사용성을 높일 수 있는 프로그래밍 방식
  • 실제 사용되는 참조 자료형으로의 변환은 컴파일러가 검증하므로 안정적인 프로그래밍 방식
  • 컬렉션 프레임워크에서 많이 사용됨
  • 제네릭클래스 <적용할타입> 변수 = new 제네릭클래스<적용할타입>();

 

타입 매개변수 설명
E 원소 (Element)
K 키 (Key)
N 숫자 (Number)
T 타입 (Type)
V 값 (Value)

제네릭의 제약

  • 기본 타입을 제네릭 인수로 사용 불가
  • 정적 제너릭 타입 금지
  • 제너릭 타입의 인스턴스화 금지. 즉, new T() 등 금지
  • 제네릭 타입의 배열 생성 금지
  • 실행 시간에 제네릭 타입 점검 금지 예를 들어, a instance of ArrayList
  • 제네릭 클래스의 객체는 예외로 던지거나 잡을 수 없다.
  • 제네릭의 서브타입 허용 않음

사용 예시

Object타입으로 모든 자료형 사용하기

// 한식 일식 타입을 모두 담을 수 있는 Food 클래스 정의
public class Food {
    // 모든 자료형을 받을 수 있는 Object 타입으로 정의
    // 이 경우 사용시 캐스팅 필요
    private Object type;

    public Object getType() {
        return type;
    }

    public void setType(Object type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return "Food [type=" + type + "]";
    }
}

// 한식 클래스 정의
public class Korean {
    @Override
    public String toString() {
        return "한식";
    }
}

// 동작
public class FoodTest {

    public static void main(String[] args) {
        Food food = new Food();

        food.setType(new Korean());

        // food.getType이 반환하는 타입은 Object이기 때문에 캐스팅 필요
        Korean korean = (Korean)food.getType();

        System.out.println(korean); // 한식
    }

}

제네릭 타입 사용

// 제네릭 클래스 T는 type의 약자, 자료형 매개변수
public class Food<T> {
    // T는 Object타입으로 변환
    private T type;

    public T getType() {
        return type;
    }

    public void setType(T type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return "Food [type=" + type + "]";
    }
}

public class FoodTest {

    public static void main(String[] args) {
        // 해당영역을 보고 컴파일러가 캐스팅작업 수행
        Food<Korean> food = new Food<Korean>();

        food.setType(new Korean());

        // food를 선언 할 때 Korean 타입을 사용한다고 작성했기 때문에
        // 캐스팅 작업 불필요
        Korean korean = food.getType();

        System.out.println(korean);
    }

}

제한된 타입의 제네릭 메서드

// Type을 상속받은 클래스만 사용 가능하게 제한
public class Food<T extends Type> {
    private T type;

    public T getType() {
        return type;
    }

    public void setType(T type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return "Food [type=" + type + "]";
    }
}

public class Korean extends Type{
    @Override
    public String toString() {
        return "한식";
    }
}

public class Japanese extends Type{
    @Override
    public String toString() {
        return "일식";
    }
}

public class Chinese {
    @Override
    public String toString() {
        return "중식";
    }
}

public class FoodTest {

    public static void main(String[] args) {
        Food<Korean> food = new Food<Korean>();
        food.setType(new Korean());
        Korean korean = food.getType();

        Food<Japanese> food2 = new Food<Japanese>();
        food2.setType(new Japanese());        
        Japanese japanese = food2.getType();

        // Chinese클래스는 Type을 상속받지 못해 사용 불가능
        // Bound mismatch: The type Chinese is not a valid substitute for the bounded parameter <T extends Type> of the type Food<T>
        Food<Chinese> food3 = new Food<Chinese>();
        food3.setType(new Chinese());
        Chinese chinese = food3.getType();
    }

}

자료형 매게변수 T

  • type의 의미로 T를 많이 사용
  • <T>에서 <>는 다이아몬드 연산자
  • static 키워드에서는 사용 불가
    T는 생성될 때(new) 타입이 결정되는데 static은 인스턴스 생성과 상관없이 메모리를 잡기 때문에 사용불가
  • ArrayList<String> list = new ArrayList<>();
    다이아몬드 연산자 내부에서 자료형 생략 가능
  • 제네릭에서 자료형 추론(자바 10부터 가능)
    ArrayList<String> list = new ArrayList<String>();
    var list = new ArrayList<String>();

제네릭에서 대입된 자료형을 명시하지 않는 경우

Food food = new Food();
food.setType(new Korean());
// food의 타입(<Korean>)을 명시하지 않았기 때문에 형변환 필요
Korean korean = (Korean)food.getType();
반응형