본문 바로가기
JAVA

[JAVA] - 자바 제네릭 프로그래밍이란?

by nam_ji 2024. 6. 14.

https://butter-shower.tistory.com/88

 

[Java] 제네릭 프로그래밍

제네릭 프로그래밍(Generic Programming) 이란? 작성한 코드를 다양한 타입의 객체에 대해 재사용한다는 객체 지향 기법이다. 제네릭(Generic) : 클래스를 정의할 때 구체적인 타입을 적지 않고 변수 형

butter-shower.tistory.com

 

자바 제네릭 프로그래밍

제네릭 프로그래밍이란?

  • 작성한 코드를 다양한 타입의 객체에 대해 재사용 한다는 객체 지향 기법입니다.
  • 제네릭 (Generic) :  클래스를 정의할 때 구체적인 타입을 적지 않고 변수 형태로 적어 놓는 것입니다. 객체를 생성할 때 구체적인 타입을 기재합니다.

제네릭 클래스 설계

1. 선언

class 클래스이름<타입매개변수> {
    // 메서드나 필드에 필요한 타입을 타입 매개변수로 나타낸다.
    필드;
    메서드;
}

 

  • 타입 매개변수는 객체를 생성할 때 구체적인 타입으로 대체합니다.

2. 전형적인 타입 매개변수

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

3. 제네릭 객체 생성

    • 제네릭클래스 <적용할타입> 변수 = new 제네릭클래스<적용할타입>();
      							|_ 생략 가능

제네릭의 제약

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


제네릭 메서드

1. 의미와 선언 방법

  • 타입 매개변수를 사용하는 메서드
  • 제네릭 클래스 뿐만 아니라 일반 클래스의 멤버도 될 수 있음
  • 제네릭 메서드를 정의할 때는 타입 매개변수를 반환 타입 앞에 위치
  • 제네릭 메서드를 호출할 때는 구체적인 타입 생략 가능 -> 다이아몬드 연산자 : <>
  • JDK7과 JDK8의 경우 익명 내부 클래스에서는 다이아몬드 연산자 사용 불가
  • JDK9부터 익명 내부 클래스에서도 다이아몬드 연산자 사용 가능

2. 정의와 호출 방법

class InstanceTypeShower {
	int showCount = 0;
    
    public <T> void showInstanceType(T ins) {
    	System.out.println(ins);
        showCount++;
    }
    
    void showPrintCount() { ... }
    
}
public static void main(String[] args) {
	AAA aaa = new AAA();
    BBB bbb = new BBB();
    
    InstanceTypeShower shower = new InstanceTypeShower();
    shower.<AAA>showInstanceType(aaa);
    shower.<BBB>showInstanceType(bbb);
    shower.showPrintCount();
}

 

  • 제네릭 메서드의 호출과정에서 전달되는 인자를 통해서 제네릭 자료형을 결정할 수 있으므로 자료형 표현 생략 가능합니다.

3. 제네릭 메서드와 둘 이상의 자료형

class InstanceTypeShower2 {
	public <T, U> void showInstanceType (T instance1, U instance2) {
    	System.out.println(instance1);
        System.out.println(instance2);
    }
}
  • T, U와 같은 문자는 상징적입니다. 따라서 타 문자로도 대체 가능합니다.
public static void main(String[] args) {
	AAA aaa = new AAA();
    BBB bbb = new BBB();
    
    InstanceTypeShower2 shower = new InstanceTypeShower2();
    shower.<AAA, BBB>showInstanceType(aaa, bbb);
    shower.showInstanceType(aaa, bbb);
}
  • 마찬가지로 메서드 호출 시, 자료형의 정보는 생략 가능합니다.

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

1. 제네릭 타입에 대한 범위 제한

  • 사용 방법
    • <T extends 특정 클래스 혹은 인터페이스> 반환타입 메서드이름(...){...}
    • T가 상속 또는 구현하는 클래스의 자료형이 되어야 함을 명시합니다.
    • 주의 : 부모 인터페이스인 경우도 implements가 아니라 extends를 사용합니다.
    • public static <T extends UpperClass> void showInstanceANcestor (T param) {
      	param.showYourAncestor();
      }
    • public static <T extends SimpleInterface> void showInstanceName (T param) {
      	param.showYourName();
      }

제한된 메서드와 배열

1. 제네릭 메서드로의 배열 전달

  • 배열도 인스턴스이므로 제네릭 매개변수에 전달 가능합니다.
  • 하지만 이렇게 전달을 하면 다음과 같은 문장을 쓸 수 없습니다.
    • System.out.println(arr[i]);
  • 다음과 같이 매개변수를 선언하면, 매개변수에 전달되는 참조값을 배열 인스턴스의 참조값으로 제한할 수 있습니다.
    • T[] arr
  • 이렇게 선언하면 참조 값을 배열 인스턴스의 참조 값임이 보장되므로 [] 연산을 허용합니다.
    • public static <T> void showArrayData(T[] arr) {
      	for (int i = 0; i < arr.length; i++) {
          	System.out.println(arr[i]);
          }
      }
    • [] 연산이 필요하다면 매개변수의 선언을 통해서 전달되는 참조 값을 배열의 참조 값으로 제한해야 합니다.

와일드 카드 타입

1. 제네릭타입<?> : Unbounded wildcards (제한 없음)


2. 제넥릭타입<? extends 상위 타입>

  • Upper Bounded Wildcards (상위 클래스 제한)
  • 상위 타입을 상속하는 클래스의 인스턴스라면 무엇이든지 참조 가능

3. 제네릭타입<? super 하위 타입>

  • Lower Bounded Wildcards (하위 클래스 제한)
  • 하위 타입이 상속하는 클래스의 인스턴스라면 무엇이든지 참조 가능

제네릭 클래스의 상속

1. 타입 파라미터는 자식 클래스에도 기술해야 함

  • public class ChildProduct<T, M> extends Product<T,M> {...}
  • 인스턴스 생성 시 자료형 지정
    • ChildProduct<String, String> myChild = new ChildProduct<String, String>();
      ChildProduct<Integer, Integer> myChild = new ChildProduct<Integer, Integer>();

2. 추가적인 타입 파라미터를 가질 수 있음

public class ChildProduct<T, M, C> extends Product<T, M> {...}

3. 제네릭 클래스의 자료형을 결정해서 상속하는 것도 가능

public class ChildProduct extends Product<String, Integer> {...}

제네릭 타입의 구현

1. 제네릭 인터페이스를 구현할 경우, 제네릭 인터페이스를 구현한 클래스도 제네릭

  • 제네릭 인터페이스를 구현할 경우, 제네릭 인터페이스를 구현한 클래스도 제네릭
    • interface MyInterface<T> {
      	public T myFunction(T item);
      }
      제네릭 인터페이스
    • class MyImplement<T> implements MyInterface<T> {
      	public T myFunction(T item) {
          	return item;
          }
      }
      제네릭 인터페이스 구현 모델, 일반적인 인터페이스의 구현과 같습니다.
    • class MyImplement implements MyInterface<String> {
      	public T myFunction(String item) {
          	return item;
          }
      }
      제네릭 인터페이스 구현 모델, 제네릭 인터페이스의 자료형을 지정해서 구현하는 것도 가능합니다.