JAVA

[JAVA] 제네릭(Generic)한 클래스를 사용하면, 어떤 이점이 있을까?

GHM 2023. 1. 5. 00:35
  • 제네릭의 정의
  • 제네릭의 역할
  • 제네릭의 특징 및 장점
  • 와일드카드
  • 제네릭 메서드
  • 제네릭 사용 예시

 

제네릭이란?

Generics add stability to your code by making more of your bugs detectable at compile time. – Oracle Javadoc

 

  • 클래스 내부에서 사용할 데이터 타입을 나중에 지정하는 기법 (개발 시점이 아닌 사용 시점에 타입지정)
  • 런타임 시 발생할 수 있는 오류를 컴파일 과정에서 사전 차단하는 안전장치

 

어떤 역할을 하나?

: 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시 타입체크를 해주는 역할을 한다.

 

특징

  • 제네릭은 데이터 타입과 관련이 있다
  • 객체를 생성하거나 메서드를 호출할 때, 명시적으로 타입을 지정한다

장점

  • 클래스 개발 시점에 T로 타입을 설정해놓고 인스턴스 사용 시점에 타입을 유연하게 지정할 수 있어서,
    코드의 재사용성이 높아지고, 유지보수에 용이하다
  • 컴파일 시 타입체크로 타입의 안정성이 보장돼, 데이터 가져올 때 형 변환이 필요없다

 

 

타입 안정성(type safety)이란?

  • 의도하지 않은 타입의 객체가 저장되는 것을 막는 것
  • 저장된 객체를 꺼내올 때, 다른 타입으로 잘못 형 변환하여 발생할 수 있는 오류를 줄이는 것

 

 

제네릭 와일드 카드

: 총 3가지의 형태가 있고, 주로 메서드의 매개 변수로만 사용되며 물음표로 표현된다

  1. 제네릭 타입<?> : 타입 파라미터를 대치하는 것으로 모든 클래스나 인터페이스 타입이 올 수 있다
  2. 제네릭 타입<? extends 상위타입> : 와일드 카드의 범위를 특정 객체의 하위 클래스에서만 올 수 있게 범위를 제한한다
  3. 제네릭 타입<? super 하위타입> : 와일드 카드의 범위를 특정 객체의 상위 클래스에서만 올 수 있게 범위를 제한한다
  • 2,3 번을 Bounded Wildcards라고 부르고, 매개 변수로 넘어오는 제네릭 타입의 경계를 지정한다

 

제네릭 메서드

  • 제네릭 메서드를 정의할 때는 리턴타입이 무엇인지는 상관없이, 제네릭 메서드라는 것을 컴파일러에게 알려줘야 한다
  • 메서드의 리턴타입 앞에 <>으로 제네릭 타입임을 선언해야 한다
  • 제네릭 메서드에도 Bounded Wildcards 사용 가능
  • 제네릭 클래스가 아닌 일반 클래스 내부에서도 제네릭 메서드를 정의할 수 있다
  • 클래스에 지정된 타입 파라미터와 제네릭 메서드에 정의된 타입 파라미터는 상관이 없다

제네릭 사용 예시 1

package test.generics;

public class Generic<T> {
	private T sample;	// 1

	public T getSample() {
		return sample;
	}

	public void setSample(T sample) {
		this.sample = sample;
	}

	public void showDataType() {
		if (sample instanceof String) {
			System.out.println("data type : String");
		} else if (sample instanceof Integer) {
			System.out.println("data type : Integer");
		}
	}

	public static void main(String[] args) {
		// 2	
		Generic<String> stringType = new Generic<>();	// 객체 생성 시점(사용 시점)에 타입 지정
		Generic<Integer> integerType = new Generic<>();

		stringType.setSample("Hi, Generic!");
		integerType.setSample(123);

		stringType.showDataType(); // data type : String
		integerType.showDataType(); // data type : Integer
	}
}
  1. Generic 클래스의 멤버변수인 sample은 아직 존재하지 않는 T라는 타입을 가진다.
  2. T 타입은 Generic 클래스의 인스턴스가 생성될 때 < > 안에 명시해 줌으로써 결정된다.

: 이렇게 객체 생성 시에 타입이 결정되므로, 의도하지 않은 타입의 객체 저장을 막을 수 있다.

 

 

제네릭 사용 예시 2

// 제네릭 사용 X
List list = new ArrayList();	
list.add("Hello! Generic");
String str = (String) list.get(0);  // 데이터를 꺼내올 때, 형 변환 불가피


// 제네릭 사용
List<String> list = new ArrayList<>();
list.add("Hello! Generic");
String str = list.get(0);  // 데이터를 꺼내올 때, 형 변환 필요 X


: 데이터를 가져올 때,  형 변환의 번거로움을 줄일 수 있다.