클래스와 인터페이스 선언에 타입 매개변수가 쓰이면, 이를 제네릭 클래스 혹은 제네릭 인터페이스라 한다.
...
제네릭 클래스와 제네릭 인터페이스를 통틀어 제네릭 타입이라고 한다.
-클래스와 인터페이스 선언에 타입 매개변수가 쓰이면, 이를 제네릭 타입이라고 한다.
List는 제네릭 인터페이스
List의 타입 매개변수는 E이고,
List 인터페이스의 풀 네임은 List<E>임
-각각의 제네릭 타입은 매개변수화 타입을 정의한다
List<String>은 List<E>의 매개변수화 타입임
-제네릭 타입을 하나 정의하면 그에 딸린 로 타입도 함께 정의된다
List<E>의 로 타입은 List임
예외적인 케이스를 제외하면 명확히 타입을 명시하도록 하자
List<Object> 는 명확하게 타입을 제시한 경우임
아래와 같이 List<E>의 로타입, List를 사용해보자
타입 매개변수를 명시하지 않았기 때문에 test는 로타입임
public static void main(String[] args) {
List test = new ArrayList();
test.add("no1");
test.add(1);
}
String과 int를 둘 다 담을 수 있음
BUT 컴파일러가 객체의 타입을 추측하기 어려워서 제네릭이 주는 타입의 안정성을 잃을 수 있음
public class Raw {
private static void unsafeAdd(List list, Object o){
list.add(o);
}
public static void main(String[] args) {
List<String> a = new ArrayList<>();
unsafeAdd(a, 42);
String s2 = a.get(0);
}
}
warn : Raw use of parameterized class 'List'
매개변수화된 클래스를 raw로 사용하고 있음
컴파일러가 int type인 a.get(0)
을 String으로 형변환 하려 할 때 에러가 발생함
if(List.class == List.class){
//
}
List<String> a = new ArrayList<>();
if(a instanceof Set){
Set<?> s = (Set<?>) a;
for(Object o:s){
if(o instanceof String){
// true
}
}
}
Type Safety한 제네릭 타입으로 만들어 형변환하는 코드를 없애자
참고링크
암묵적 규칙이기 때문에 반드시 따를 필요는 없음
한 글자일 필요도 없다
E-Element
K-Key
N-Number
T-Type
V-Value
제네릭 타입을 받는 예시 List.of를 뜯어보자
아래와 같이 Element를 제네릭으로 명시하여 같은 타입의 객체 n개를 입력받도록 되어 있음
또 파라미터로 받는 Element의 타입과 리스폰스로 주어지는 엘리먼트의 타입이 완전히 동일함
Object 클래스는 모든 자바 클래스의 최상위 클래스임
자식 객체는 부모 타입에 대입할 수 있기 때문에 모든 자바 객체는 Object 타입에 저장될 수 있음
public class Box {
private Object object;
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
public static void main(String[] args) {
Box box = new Box();
box.setObject("hello");
String str = (String) box.getObject();
}
}
제네릭으로 수정해봄
public class Box2<T> {
private T t;
public T get(){
return t;
}
public void set(T t){
this.t = t;
}
public static void main(String[] args) {
Box2<String> box2 = new Box2<String>();
box2.set("hello");
String str2 = box2.get();
}
}
제네릭 클래스로 만들어서 생성시에 타입 파라미터로 명시한 객체만 받을 수 있다.
선언한 순간부터 나는 한 타입만 받을 거야 하는것!
따라서 형 변환이 필요하지 않음
public class Calculator<E> {
private StringBuilder expression;
public Calculator() {
expression = new StringBuilder();
}
public void add(E e){
expression.append("+"+e.toString());
}
public void minuse(E e){
expression.append("-"+e.toString());
}
public String expression(){
if(expression.charAt(0)=='+'){
return expression.substring(1);
}else{
return expression.toString();
}
}
}