JAVA 제네릭스(Generics)

Dev.Shinny·2022년 11월 21일
0

자바의 정석

목록 보기
18/18

제네릭스

다양한 타입의 객체들을 다루는 메서드나 컬레션 클래스에 컴파일 시의 타입 체크를 해주는 기능이다.

객체 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움이 줄어든다. 타입 안전성을 높인다는 것은 의도하지 않은 타입의 객체가 저장되는 것을 막고, 저장된 객체를 꺼내올 때 원래의 타입과 다른 타입으로 잘못 형변환되어 발생할 수 있는 오류를 줄여준다는 뜻이다.

제니릭스가 도입되기 이전에는 다양한 종류의 타입을 다루는 메서드의 매개변수나 리턴타입을 Object 타입의 참조변수를 사용했고, 그로 인해 형변환이 불가피했다.

import java.util.ArrayList;

public class GenericTest {
	public static void main(String[] args) {
		
		ArrayList list = new ArrayList();
		list.add(10);
		list.add(20);
		list.add("30");
		
		Integer i = (Integer)list.get(2); // 실행시 에러 발생, 컴파일은 가능. 
		
		System.out.println(list);
		
	}
}

Exception in thread "main"java.lang.ClassCastException : class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
at ch01.GenericTest.main(
GenericTest.java:13)

정수형 타입만 저장하는 ArrayList를 생성하려고 String 타입이 저장되었다. 이를 정수형으로 형변환하여 꺼내오려고 했더니 컴파일 시 체크가 불가능해, 컴파일이 실행되고 오류가 발생했음을 알 수 있다. 이런 오류를 방지하기 위한 기능이 제네릭이다. 컴파일러에게 타입 정보를 제공함으로서 컴파일러가 타입을 체크해준다.

	ArrayList<Integer> list = new ArrayList<>();
	list.add(10);
	list.add(20);
	list.add("30");

위처럼 제네릭을 이용해 타입을 지정해주면, <The method add(Integer) in the type ArrayList<Integer> is not applicable for the arguments>라고 오류 메시지가 뜬다.

제네릭 클래스

제네릭 타입은 클래스와 메서드에 선언할 수 있다. 지네릭 클래스는 클래스 옆에 <T>를 붙이면 된다. 그리고 기존 Object 타입이었던 것들은 모두 'T'로 바꾼다.

class box<T> {
	T item;
    
    void setItem(T item) { this.item = item; }
    T getItem() { return item; }
}

T는 '타입 변수'라고 하며 Type의 첫 글자를 따온 것이다. 타입 변수는 T가 아닌 다른 것을 사용해도 좋다.

T : Type
E : Element
K : Key
V : Value

상황에 맞게 의미있는 문자를 선택하여 사용. 이들은 기호의 종류만 다를 뿐 '임의의 참조형 타입'을 의미한다.

예제_(feat fastcampus)

3D 프린터를 만들고자 한다. 재료는 power와 plastic이 있다. 3D 프린터 클래스는 재료 받아 오고, 어떤 재료를 받아왔는지 toString()을 오버라이딩해 출력한다.

plastic

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

powder

public class Powder {

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

Generic3DPrinter

public class Generic3DPrinter<T>{
	private T material;

	public T getMaterial() {
		return material;
	}

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

Generic3DPrinterTest

public class Generic3DPrinterTest {

	public static void main(String[] args) {
		
		Generic3DPrinter<Powder> powderPrinter = new Generic3DPrinter<Powder>();
		powderPrinter.setMaterial(new Powder());
		System.out.println(powderPrinter);
		
		Generic3DPrinter<Plastic> plasticPrinter = new Generic3DPrinter<Plastic>();
		plasticPrinter.setMaterial(new Plastic());
		System.out.println(plasticPrinter);
		
	}
}

< T extends 클래스 >

상위 클래스의 필요성

  • T 자료형의 범위를 제한 할 수 있음
  • 상위 클래스에서 선언하거나 정의하는 메서드를 활용할 수 있음
  • 상속을 받지 않는 경우 T는 Object로 변환되어 Object 클래스가 기본으로 제공하는 메서드만 사용가능
public abstract class Material {
	public abstract void doPrinting();
}

public class Powder extends Material{		
	public void doPrinting() {
    	...
    }
    ...
}

public class Plastic extends Material{		
	public void doPrinting() {
    	...
    }
    ...
}

public class GenericPrinter<T extends Material> {
	...
}
profile
Hello I'm Shinny. A developer who try to enjoy the challenge.

0개의 댓글