[Java] 제네릭 (Generic) 1
22. 제네릭 (Generic)
☑️ 제네릭은 왜 필요할까?
문제점1. 코드의 중복
- 다양한 타입을 담는 박스가 필요할 때, 각각의 타입별로 클래스를 새로 만들어야 한다.
Integer
타입을 보관하는 IntegerBoxpublic class IntegerBox { private Integer value; public void set(Integer value) { this.value = value; } public Integer get() { return value; } }
String
타입을 보관하는 StringBoxpublic class StringBox { private String value; public void set(String value) { this.value = value; } public String get() { return value; } }
문제점2. Object 타입의 박스에서 발생하는 타입 안전성 문제
다형성을 활용하여 모든 타입의 부모인
Object
로 박스를 만들어보자.public class ObjectBox { private Object value; public void set(Object object) { this.value = object; } public Object get() { return value; } }
Object
타입의 박스로 IntegerBox와 StringBox를 만들면 코드의 중복을 제거하고, 기존 코드를 재사용할 수 있게 된다.public class BoxMain2 { public static void main(String[] args) { ObjectBox integerBox = new ObjectBox(); integerBox.set(10); Integer integer = (Integer) integerBox.get(); ObjectBox stringBox = new ObjectBox(); stringBox.set("hello"); String str = (String) stringBox.get(); } }
하지만, 잘못된 타입의 인수가 전달되어 문제가 발생할 수 있다.
integerBox.set("문자100"); Integer result = (Integer) integerBox.get();
- 예외 발생
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')
☑️ Generic 적용
public class GenericBox<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
- Generic 클래스는
(타입 매개변수)를 선언해야 한다. - 클래스 내부에는 T 타입이 필요한 곳에
T value
처럼 작성하면 된다. - T라는 타입 매개변수에 String, Integer와 같은 타입 인자를 전달해서 사용할 타입을 결정한다.
예제
public class BoxMain3 {
public static void main(String[] args) {
GenericBox<Integer> integerBox = new GenericBox<>();
integerBox.set(10);
//integerBox.set("100"); // Integer 타입만 허용, 컴파일 오류
Integer integer = integerBox.get(); // Integer 타입 반환(캐스팅 X)
GenericBox<String> stringBox = new GenericBox<>();
stringBox.set("hello");
String str = stringBox.get();
}
}
- 생성 시점에
GenericBox<Integer>
,GenericBox<String>
와 같이<>
안에 타입 매개변수를 정의하면 된다. Integer
타입으로 선언된 integerBox는Integer
숫자만 담을 수 있고,String
타입으로 선언된 stringBox는String
문자열만 담을 수 있다.- 타입 인자로 기본형(
int
,double
..)은 사용할 수 없기 때문에 래퍼 클래스(Integer
,Double
..)을 사용해야 한다.
- 타입 인자로 기본형(
get()
메서드를 쓸 때도 타입 캐스팅 없이 각각의 타입으로 조회할 수 있다.
타입추론
GenericBox<Integer> integerBox = new GenericBox<Integer>(); // [1]
GenericBox<Integer> integerBox2 = new GenericBox<>(); // [2]
- 보통의 경우 [1]과 같이 객체 생성 시
<Integer>
를 보고 필요한 타입 정보를 얻는다. - 하지만, [2]와 같이 타입정보를 생략 하여도 자바는 스스로 타입 정보를 추론한다.
- 읽을 수 있는 타입 정보가 주변에 있는 경우만 추론 가능하다.
Raw Type
GenericBox integerBox = new GenericBox();
integerBox.set(10);
Integer result = (Integer) integerBox.get();
<>
에 타입 인자를 지정하지 않고 사용한다면GenericBox<Object>
으로 사용되는 것이다.- 제네릭이 없던 시절의 코드와 하위 호환이 필요해서 어쩔 수 없이 이런 Raw Type을 지원한다고 한다.
김영한의 실전 자바 중급 2편을 참고하였습니다