#1 Genrics

charco·2021년 5월 17일
0

나도TIL

목록 보기
2/55

Generics

메서드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능이다.
쉽게 말하면
지네릭스는 타입 파라미터다.
원래는 변수를 파라미터로 사용했는데
타입(자료형)도 파라미터로 사용할 수 있다는 말이다.
가장 흔히 쓰는 자료구조인 ArrayList가 대표적인 예다.

class myGenericClass<T>{
    T item;

    myGenericClass(T item){
        this.item = item;
    }

    public T getItem(){
        return item;
    }
}

T를 타입파라미터로 받고
T타입의 변수가 선언되어 있는 클래스다.
만약 T(타입 파라미터) 에 String을 줬다면

MyGenericClass<String> myGenericsClass = new MyGenericClass<>("STRING");
        System.out.println(myGenericsClass.getItem()); //결과: STRING

컴파일러가 이 코드를 아래와 같이 컴파일 해준다.

class myGenericClass<String>{
    String item;

    myGenericClass(String item){
        this.item = item;
    }

    public String getItem(){
        return item;
    }
}

주의할 것은 static 메서드나 필드에서 타입파라미터를 사용할 수 없다는 것이다. static 은 해당 클래스의 모든 인스턴스에서 공유되는 것이기 때문이다.

Generics의 제한

지네릭스에서도 다형성을 적용할 수 있을까?
가능하다.
Fruit라는 클래스를 상속받는 Apple, Grape 클래스가 있다고 해보자.
타입 파라미터를 Fruit의 자손들만 받도록 제한할 수 있다.

        MyGenericClass<Fruit> myGenericClass = new MyGenericClass<Apple>();
        MyGenericClass<Fruit> myGenericClass2 = new MyGenericClass<Grape>();

타입파라미터에 extends 를 추가하면 된다.

class MyGenericClass<T extends Fruit>{
    T item;
    MyGenericClass(){}

    MyGenericClass(T item){
        this.item = item;
    }

    public T getItem(){
        return item;
    }
}

인터페이스의 경우에도 implements 가 아닌 extends를
사용하니 주의하자.

Wild Card

List 의 자손클래스의 인스턴스를 받아서
forEach로 요소를 하나씩 출력하는 메서드가 있다고 해보자.

public void printList(List<String> list){
        list.forEach(i-> System.out.println(i));
    }

이 메서드는 String 타입파라미터를 받는 List 만 파라미터로 받을 수 있다.
Integer나 다른 타입으로 받고 싶으면 어떻게 할까?

public void printList(List<String> list)
public void printList(List<Integer> list)
public void printList(List<Boolean> list)

이런 식으로 오버로딩해주면 될까?
하지만 컴파일 에러가 난다.
지네릭스는 컴파일러가 컴파일 할때만 사용하고 제거해버리기 때문이다.
때문에 위의 메서드들이 모두 같은 파라미터를 받는다고 보는 것이다.

String 과 Integer, Boolean 은 모두 Object의 자손이다.
때문에 아래의 코드처럼 바꿔주면 된다.

public void printList(List<?> list){
        list.forEach(i-> System.out.println(i));
    }

이게 바로 와일드 카드다.
만약 와일드카드의 자손들만 받도록 제한하고 싶으면
? 뒤에 extends를 붙혀주면 된다.
이것을 상한 제한이라고 한다.

public void printList(List<? extends Fruit> list){
        list.forEach(i-> System.out.println(i));
    }

반대로 와일드카드의 자손이 아닌 모든 부모들만 받도록 제한 할 수도 있다. 이것은 하한 제한이라고 한다.

public void printList(List<? super Apple> list){
        list.forEach(i-> System.out.println(i));
    }

Generic Method

위에서 static메서드는 타입파라미터를 사용하지 못한다고 했다.
하지만 지네릭 메서드를 이용하면 가능하다.
클래스에 타입파라미터를 정의한 것처럼
메서드에도 똑같이 가능하다는 것이다.

static <T> T myStaticMethod(T item){
        return item;
    }

정말 쓸모없는 메서드이지만 설명을 위해 작성했다.
만약 클래스의 타입파라미터 이름과 지네릭 메서드의 타입 파라미터 이름이 같아도 둘은 구분된다.

class MyGenericClass<T>{

    T item;
	//에러! this의 T와 메서드의 T는 다르다.
     public <T> T myStaticMethod(T item){
        this.item= item;
    }

}

자바에 내장돼있는 클래스들과 인터페이스들은 대부분
지네릭을 사용하기 때문에 알아두면 라이브러리를 해석하는데
많은 도움이 될 듯 하다.

profile
아직 배우는 중입니다

0개의 댓글