모든지 기록하자!

[Java] 제네릭 (generic) 본문

Java

[Java] 제네릭 (generic)

홍크 2021. 5. 20. 23:18
728x90

제네릭이란?

어떤 값이 하나의 참조 자료형이 아닌 여러 참조 자료형을 사용할 수 있도록 프로그래밍하는 것을

제네릭(generic)  프로그래밍이라고 한다. 제네릭 프로그램은 참조 자료형이 변환될 때 검증을 컴파일러가 하기 때문에 안정적이다. 

3D 프린터 예제로 제네릭에 대해 알아보자

public class TreeDPrinter{

	private Powder material; // 재료가 파우더일 때

	public Powder getMaterial() {
		return material;
	}

	public void setMaterial(Powder material) {
		this.material = material;
	}

플라스틱 액체를 재료로 사용하는 프린터를 구현해 보자

public class TreeDPrinter{

	private Plastic material; // 재료가 플라스틱일 때

	public Plastic getMaterial() {
		return material;
	}

	public void setMaterial(Plastic material) {
		this.material = material;
	}

재료만 바뀌고 프린터 기능이 동일하다면 프린터 클래스를 두 개 만드는 것은 비효율적이다.

이런 경우엔 어떤 재료든 쓸 수 있도록 material 변수의 자료형을 Object로 사용 할 수 있다.

Object는 모든 클래스의 최상위 클래스이므로 모든 클래스는 Object로 변환할 수 있다.

Object를 활용하여 만든 코드를 보자

public class TreeDPrinter{

	private Object material;

	public Object getMaterial() {
		return material;
	}

	public void setMaterial(Object material) {
		this.material = material;
	}

material 변수의 자료형을 Object로 선언한 ThreeDPrinter에 파우더를 재료로 사용하는 코드를 보자

ThreeDPrinter printer = new TreeDPrinter();

Powder p1 = new Powder();
printer.setMaterial(p1); // 자동 형 변환된다.

Powder p2 = (Powder)printer.getMaterial(); // 직접 형 변환 해야한다.

setMaterial() 메서드를 활용해서 Powder를 재료로 선택할 때는 매개변수 자료형이 Object 이므로 자동으로 형 변환된다. 하지만 반환형이 Object인 getMaterial() 메서드로 Powder 자료형 변수를 반환받을 때는

반드시 형 변환을 해줘야 한다. 어떤 변수가 여러 참조 자료형을 사용할 수 있도록 Object 클래스를 사용하면 다시 원래 자료형으로 반환해 주기 위해 매번 형 변환을 해야 하는 번거로움이 있다.

이러한 경우에 사용하는 프로그래밍 방식이 제네릭이다. 여러 참조 자료형이 쓰일 수 있는 곳에 특정한 자료형을 지정하지 않고, 클래스나 메서드를 정희한 후 사용하는 시점에 어떤 자료형을 사용할 것인지 지정하는 방식이다.

public class GenericPrinter<T>{

	private T material;

	public T getMaterial() {
		return material;
	}

	public void setMaterial(T material) {
		this.material = material;
	}

여러 자료형으로 바꾸어 사용할 meterial 변수의 자료형을 T라고 썼다. 이때 사용하는 T를 자료형 매개변수라고 부른다. 클래스 이름을 GenericPrinter <T>라고 정의하고 나중에 클래스를 사용할 때 T 위치에 실제 사용할 자료형을 지정한다. 클래스의 각 메서드에서 해당 자료형이 필요한 부분에는 모두 T문자를 사용해서 구분한다.

제네릭 클래스 사용 예제

public class Powder{

	public String toString() {
		return "재료는 Powder 입니다.";
	}
	
	public void doPrinting() {
		System.out.println("Powder 재료로 출력합니다.");
	}
public class Plastic{

	public String toString() {
		return "재료는 Plastic 입니다.";
	}

	public void doPrinting() {
		System.out.println("Plastic 재료로 출력합니다.");
	}
}

파우더와 플라스틱 액체를 재료로 모형을 출력하는 프린터를 제네릭 클래스로 정의하면

public class TreeDPrinter<T>{

	private T material; // T 자료형으로 선언한 변수

	public T getMaterial() { // T 자료형 변수 meterial을 반환하는  제네릭 메서드
    
		return material;
	}

	public void setMaterial(T material) {
		this.material = material;
	}

	public String toString() {
		return material.toString();
	}

 

public class TreeDPrinterTest {

	public static void main(String[] args) {

		TreeDPrinter<Powder> printer = new TreeDPrinter<Powder>(); // Powder형으로
		printer.setMaterial(new Powder());                        // ThreeDPrinter 클래스 생성
		Powder powder = printer.getMaterial();
		System.out.println(printer);
		
		TreeDPrinter<Plastic> printerPlastic = new TreeDPrinter<Plastic>(); // Plastic형으로
		printerPlastic.setMaterial(new Plastic());                     // ThreeDPrinter 클래스 생성
		Plastic plastic = printerPlastic.getMaterial();
		System.out.println(printerPlastic);

출력결과

T 자료형에 사용할 자료형을 제한하는 <T extends 클래스>

제네릭 클래스에서 T 자료형에 사용할 자료형에 제한을 두는 방식으로 extends 예약어를 사용할 수 있다.

Material 클래스는 추상 클래스로 정의하였고 상속받은 클래스는 doPrinting() 추상 메서드를 반드시

구현해야 한다.

public abstract class Material {

	public abstract void doPrinting();
}

Mererial을 상속받은 Powder와 Plastic 클래스 코드를 보자

public class Plastic extends Material{

	public String toString() {
		return "재료는 Plastic 입니다.";
	}

	@Override
	public void doPrinting() {
		System.out.println("Plastic 재료로 출력합니다.");
	}
}
public class Powder extends Material{

	public String toString() {
		return "재료는 Powder 입니다.";
	}

	@Override
	public void doPrinting() {
		System.out.println("Powder 재료로 출력합니다.");
	}
public class TreeDPrinter<T extends Material>{
                       // extends 예약어로 사용할 수 있는 자료형에 제한을 둔다.
	private T material;

...(생략)
}

<T extends 클래스>로 상위 클래스 메서드 사용하기

<T extends Material>로 선언하면 제네릭 클래스를 사용할 때 상위 클래스 Material에서 선언한 메서드

toPrinting()을 사용할 수 있다. <T extends Material>을 사용 하지않으면 T는 컴파일 할 때 Object 클래스로 변환된다. 이 경우에 Object 클래스가 기본으로 제공하는 메서드만 사용할수 있다. 자료형을 알 수 없기

때문이다. 

728x90
Comments