[Java]제네릭

정석용·2023년 4월 14일
0

Java

목록 보기
14/15
post-thumbnail

제네릭

제네릭이란?

데이터의 타입을 일반화한다는 것을 의미한다.

제네릭은 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법이다.
컴파일 시에 미리 타입 검사를 수행하면 다음과 같은 장점을 가진다.

  • 클래스나 메소드 내부에서 사용되는 객체의 타입 안정성을 높일 수 있다.
  • 반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있다.

JDK 1.5부터 도입된 제네릭을 사용하면 컴파일 시에 미리 타입이 정해지므로, 타입 검사나 타입 변환과 같은 번거로운 작업을 생략할 수 있게 된다.

제네릭의 선언 및 생성
제네릭의 선언.

class MyArray<T> {
    T element;
    void setElement(T element) { this.element = element; }
    T getElement() { return element; }
}

위에 예제에서 사용된 'T'를 타입 변수라고 하며, 임의의 참조형 타입을 의미한다.
타입 변수는 클래스에서뿐만 아니라 메소드의 매개변수나 반환값으로도 사용할 수 있다.

위와 같이 선언된 제네릭 클래스를 생성할 떄에는 타입 변수 자리에 사용할 실제 타입을 명시해야한다

MyArray<Integer> myArr = new MyArray<Integer>();

위처럼 실제 타입을 명시하면, 내부적으로는 정의된 타입 변수가 명시된 실제 타입으로 변환되어 처리된다. (Java SE 7 부터 인스턴스 생성 시 타입을 추정할 수 있는 경우에는 타입을 생략한다.)

제네릭의 제거 시기
자바 코드에서 선언되고 사용된 제네릭 타입은 컴파일 시 컴파일러에 의해 자동으로 검사되어 타입 변환된다. 그리고서 코드 내의 모든 제네릭 타입은 제거되어, 컴파일된 class 파일에는 어떠한 제네릭 타입도 포함되지 않게 된다.

이런 식으로 동작하는 이유는 제네릭을 사용하지 않는 코드와의 호환성을 유지하기 위해서이다.

제네릭 메소드
일반적인 선언 방법

Public <T> T genericMethod(T o) {
	...
}
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());
	}
}

클래스와는 다르게 반환타입 이전에 <>제네릭 타입을 선언한다.

즉, 클래스에서 지정한 제네릭유형과 별도로 메소드에서 독립적으로 제네릭 유형을 선언하여 쓸 수 있다.
위의 방식이 필요한 이유는 정적 메소드로 선언할 때 필요하기 때문이다.
제네릭 유형은 외부에서 지정해준다. 해당 클래스 객체가 인스턴스화 했을 때, 쉽게 말해 new 생성자로 클래스 객체를 생성하고 <> 괄호 사이에 피라미터로 넘겨준 타입으로 지정이 된다는 뜻이다.

제네릭이 사용되는 메소드를 정적메소드로 두고 싶은 경우 제네릭 클래스와 별도로 독립적인 제네릭이 사용되어야 한다.

제한된 제네릭과 와일드 카드

extends, super, ? 가 있다. ?는 와일드 카드라고 해서 쉽게 말해 알 수 없는 타입이다.

extends T : 상한 경계
? Super T : 하한 경계

: 와일드 카드

제한된 제네릭

  • 타입 문자로 사용할 타입을 명시하게 되면, 한 종류의 타입만 저장할 수 있도록 제한 하였다.
  • 하지만, 객체 생성을 할 때, 타입 변수에 모든 종류의 타입을 지정할 수 있다는 것에는 아직 변함이 없다.
  • 이렇듯, 타입 매개변수 T에 지정할 수 있는 타입의 종류를 제한할 수 있을까?
  • 일반 상속관계처럼, 이때도 extends 키워드를 사용하면 매개변수 T에 지정할 수 있는 타입의 종류를 제한할 수 있게 된다.

EX.

class FruitBox<T extends Fruit> {  // Fruit의 자손만 타입으로 지정가능
    // Logic
}

종류는 한 종류의 타입만을 담을 수 있지만, Fruit 클래스와 같거나 자손들만 담을 수 있다는 제한이 추가 되었다. (implements를 사용하지 않는다!!)

와일드 카드
EX.

public class Juicer {
    static Juicer makeJuice(FruitBox<Fruit> box) {
        
    }
}

만약에 위와 같이 Juicer 클래스 안에 static 메소드 makeJuice가 존재한다고 가정하자. static 메소드기 때문에 타입 매게변수 T를 사용할 수 없으므로 아예 제네릭을 사용하지 않거나, 위와 같이 특정한 타입을 지정해줘야 한다.

FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();

System.out.println(Juicer.makeJuice(fruitBox));  // OK
System.out.println(Juicer.makeJuice(appleBox));  // 에러

이렇게 제네릭 타입을 고정해 놓으면, 위의 코드에서 할 수 있듯이 FruitBox객체는 makejuice의 매개변수가 될 수 없으므로, 다음과 같이 여러가지 타입의 매게변수를 갖는 makejuice를 만들 수 밖에 없다.

public class Juicer {
    
    static Juicer makeJuice(FruitBox<Fruit> box) {
        
    }
    
    static Juicer makeJuice(FruitBox<Apple> box) {
        
    }
}

위와 같이 오버로딩하면, 컴파일 에러가 발생한다. 제너릭 타입이 다른 것만으로는 오버로딩이 성립하지 않기 때문이다. 이럴 때 사용하는 것이 와일드 카드이다.

와일드 카드의 제한 종류

와일드 카드의 상한 제한(upper bound) - T와 그 자손들을 구현한 객체들만 매개변수로 가능 와일드 카드의 하한 제한(lower bound) -T와 그 조상들을 구현한 객체들만 매개변수로 가능 제한 없음

래퍼런스
http://www.tcpschool.com/java/java_generic_concept
https://devlog-wjdrbs96.tistory.com/203

profile
오늘도 성장중

0개의 댓글