[effective java] (7) Generic Type

orca·2022년 11월 17일
0

effective java

목록 보기
7/8
post-thumbnail

Generic Type

클래스와 인터페이스 선언에 타입 매개변수가 쓰이면, 이를 제네릭 클래스 혹은 제네릭 인터페이스라 한다.
...
제네릭 클래스와 제네릭 인터페이스를 통틀어 제네릭 타입이라고 한다.

-클래스와 인터페이스 선언에 타입 매개변수가 쓰이면, 이를 제네릭 타입이라고 한다.

List는 제네릭 인터페이스
List의 타입 매개변수는 E이고,
List 인터페이스의 풀 네임은 List<E>

-각각의 제네릭 타입은 매개변수화 타입을 정의한다

List<String>은 List<E>의 매개변수화 타입임

-제네릭 타입을 하나 정의하면 그에 딸린 로 타입도 함께 정의된다

List<E>의 로 타입은 List임

제네릭의 장점

  1. 컴파일 시 강한 타입 체크가 가능함
  2. Type Safety하기 때문에 타입 변환이 필요 없음

Raw Type은 사용하지 말라

예외적인 케이스를 제외하면 명확히 타입을 명시하도록 하자
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으로 형변환 하려 할 때 에러가 발생함

Raw를 사용하는 경우

  1. Class 리터럴
if(List.class == List.class){
            //
 }
  1. 제네릭 타입 정보는 런타임 시 지워지기 때문에 로타입을 타입 체크 시 활용
    ex> List<E>의 정보는 런타임시 지워짐
List<String> a = new ArrayList<>();
        if(a instanceof Set){
            Set<?> s = (Set<?>) a; 
            for(Object o:s){ 
                if(o instanceof String){ 
                    // true
                }
            }
        }
  1. a가 Set의 일종이면,
  2. 와일드 카드 타입의 Set 집합으로 a를 타입캐스팅
  3. 해당 Set의 원소가 String인지 체크

이왕이면 제네릭 타입으로 만들라

Type Safety한 제네릭 타입으로 만들어 형변환하는 코드를 없애자
참고링크

제네릭 타입 파라미터 종류

암묵적 규칙이기 때문에 반드시 따를 필요는 없음
한 글자일 필요도 없다

E-Element
K-Key
N-Number
T-Type
V-Value

List.of

제네릭 타입을 받는 예시 List.of를 뜯어보자

아래와 같이 Element를 제네릭으로 명시하여 같은 타입의 객체 n개를 입력받도록 되어 있음
또 파라미터로 받는 Element의 타입과 리스폰스로 주어지는 엘리먼트의 타입이 완전히 동일함

Object vs Generic

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();
    }
}
  1. setObject는 자바의 모든 객체를 받을 수 있다.
  2. Object 타입 변수에 setObject로 받은 객체를 저장한다
  3. 따라서 getObject 호출시 Object 타입의 객체를 반환함
    Object 타입을 원래 타입의 객체로 받기 위해 Type Casting이 필요함

제네릭으로 수정해봄

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();
    }
}

제네릭 클래스로 만들어서 생성시에 타입 파라미터로 명시한 객체만 받을 수 있다.
선언한 순간부터 나는 한 타입만 받을 거야 하는것!
따라서 형 변환이 필요하지 않음

Generic 사용 예시

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();
        }
    }
}

0개의 댓글