Generic

동동주·2024년 5월 2일

CS스터디_자바

목록 보기
16/19

제네릭이란?

  • 클래스 내부에서 지정하는 것이 아닌 외부에서 사용자에 의해 지정되는 것을 의미한다. 한마디로 특정(specific) 타입을 미리 지정해주는 것이 아닌 필요에 의해 지정할 수 있도록 일반(Generic) 타입이라는 것이다.

제네릭의 장점

1) 제네릭을 사용하면 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지할 수 있다.
2) 클래스 외부에서 타입을 지정해주기 때문에 따로 타입을 체크하고 변환해줄 필요가 없다. 즉, 관리하기 편하다.
3) 비슷한 기능을 지원하는 경우 코드의 재사용성이 높아진다.

제네릭 사용방법

타입설명
<T>Type
<E>Element
<K>Key
<V>Value
<N>Number

상황별 생성방법

1. 클래스 및 인터페이스 선언

public class ClassName <T, K> { ... }
 
public class Main {
	public static void main(String[] args) {
		ClassName<String, Integer> a = new ClassName<String, Integer>();
	}
}
  • 위 코드에서 T는 String이 되고 K는 Integer가 된다.
  • 이 때 주의해야 할 점은 타입 파라미터로 명시할 수 있는 것은 참조 타입(Reference Type)밖에 올 수 없다. 즉, int, double, char 같은 primitive type은 올 수 없다는 것이다. (그래서 int형 double형 등 primitive Type의 경우 Integer, Double 같은 Wrapper Type으로 쓰는 이유가 바로 위와 같은 이유다.)
public class ClassName <T> { ... }
 
public class Student { ... }
 
public class Main {
	public static void main(String[] args) {
		ClassName<Student> a = new ClassName<Student>();
	}
}
  • 위 코드처럼 사용자가 정의한 클래스도 타입으로 올 수 있다.

2. 제네릭 클래스

class ClassName<E> {
	
	private E element;	// 제네릭 타입 변수
	
	void set(E element) {	// 제네릭 파라미터 메소드
		this.element = element;
	}
	
	E get() {	// 제네릭 타입 반환 메소드
		return element;
	}
	
}

class Main {
	public static void main(String[] args) {
		
		ClassName<String> a = new ClassName<String>();
		ClassName<Integer> b = new ClassName<Integer>();
		
		a.set("10");
		b.set(10);
	
		System.out.println("a data : " + a.get());
		// 반환된 변수의 타입 출력 
		System.out.println("a E Type : " + a.get().getClass().getName());
		
		System.out.println();
		System.out.println("b data : " + b.get());
		// 반환된 변수의 타입 출력 
		System.out.println("b E Type : " + b.get().getClass().getName());
		
	}
  • 위 코드에서는 a객체의 ClassName의 E 제네릭 타입은 String으로 모두 변환된다. 반대로 b객체의 ClassName의 E 제네릭 타입은 Integer으로 모두 변환된다.

  • 위 코드를 실행시키면 다음과 같이 출력된다.

3. 제네릭 메소드

  • 위 과정까지는 클래스 이름 옆에 예로들어 라는 제네릭타입을 붙여 해당 클래스 내에서 사용할 수 있는 E 타입으로 일반화를 했다.
  • 그러나 그 외에 별도로 메소드에 한정한 제네릭도 사용할 수 있다.
	class ClassName<E> {
	
	private E element;	// 제네릭 타입 변수
	
	void set(E element) {	// 제네릭 파라미터 메소드
		this.element = element;
	}
	
	E get() {	// 제네릭 타입 반환 메소드 
		return element;
	}
	
	<T> T genericMethod(T o) {	// 제네릭 메소드
		return o;
	}
 
	
}
 
public class Main {
	public static void main(String[] args) {
		
		ClassName<String> a = new ClassName<String>();
		ClassName<Integer> b = new ClassName<Integer>();
		
		a.set("10");
		b.set(10);
	
		System.out.println("a data : " + a.get());
		// 반환된 변수의 타입 출력 
		System.out.println("a E Type : " + a.get().getClass().getName());
		
		System.out.println();
		System.out.println("b data : " + b.get());
		// 반환된 변수의 타입 출력 
		System.out.println("b E Type : " + b.get().getClass().getName());
		System.out.println();
		
		// 제네릭 메소드 Integer
		System.out.println("<T> returnType : " + a.genericMethod(3).getClass().getName());
		
		// 제네릭 메소드 String
		System.out.println("<T> returnType : " + a.genericMethod("ABCD").getClass().getName());
		
		// 제네릭 메소드 ClassName b
		System.out.println("<T> returnType : " + a.genericMethod(b).getClass().getName());
	}
}
  • ClassName이란 객체를 생성할 때 <> 안에 타입 파라미터를 지정한다.
  • 위 코드를 실행시키면 다음과 같은 결과가 나온다.

super로 타입 지정이 안되는 이유

super 키워드로 타입을 지정하는 것은 의미가 없기 때문에 자바는 super 키워드로 타입을 지정하는 것을 허용하지 않는다.

  • 다만, super 키워드로 타입을 지정하지 않고 메서드의 인자에서 와일드 카드를 사용하는 것은 가능하다. 와일드 카드는 타입을 지정하지 않고 컴파일 시에 바운더리만 검사하기 때문이다.

출처:
https://st-lab.tistory.com/153
https://velog.io/@sunaookamisiroko/%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%97%90%EC%84%9C-super-%EC%82%AC%EC%9A%A9%EC%9D%B4-%EB%B6%88%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9D%B4%EC%9C%A0-Generic-Type-Erasure

0개의 댓글