Contents
Generic in Java
   Nov 30, 2022     6 min read

제네릭(Generic)

제네릭(Generic)이란?

  • 제네릭(Generic)은 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법을 의미한다.
  • 제네릭(Generic) Javc 5부터 새롭게 추가되었다.

  • 제네릭 구조

Untitled

  1. Class Person2가 있다. 클래스의 필드 안에 info에 데이터 타입은 명시적으로 지정되어있지 않다.

Untitled 1

  1. Person2<String> p1 = new Person2<String>(); 이라는 코드가 될 때, 즉 인스턴스화(new Person2<String>()) 할 때 < > 안에 구체적으로 명시된 타입이 class Person2<String> 이 된다. 따라서 Person2 클래스 안에 info는 String이라는 타입을 갖게 된다.

Untitled 2

  1. GenericDemo2 클래스 안에 p1의 데이터 타입도 String 타입이 지정되어야 한다.
  • 두번째 객체의 타입을 StringBuilder로 생성할때
    • 위의 예시와 같이 동작된다.

Untitled 3


제네릭 타입의 이름 정하기

<> 안에 어떤 단어가 들어가도 상관이 없긴하다.

하지만 마음대로 넣는다면 코드의 가독성이 엄청 떨어질 것이다.

따라서 일반적으로 사용하는 제네릭 타입을 알아보자.

  • E : 요소 (Element, 자바 컬렉션(Collection)에서 주로 사용된다.)
  • K : 키
  • N : 숫자
  • T : 타입
  • V : 값
  • S, U, V : 두 번째, 세 번째, 네 번째에 선언된 타입

제네릭 ? 는 무엇일까?

메소드의 매개 변수로 넘어가는 제네릭

public class WildcardGeneric<W>{
    W wildcard;

    public void setWildcard(W wildcard){
        this.wildcard = wildcard;
    }

    public W getWildcard(){
        return wildcard;
    }
}

Untitled 4

  1. callWildcardMethod() 메소드에서는 방금 만든 WildcardGeneric이라는 클래스에 String을 사용하는 제네릭한 객체를 생성한다.
  2. 생성한 객체로 wildcardStringMethod()를 호출할 때 넘겨준다.
  3. wildcardStringMethod()에서는 해당 매개 변수를 받아서 결과를 출력한다.

wildcardStringMethod() 메소드의 매개 변수는 반드시 String을 사용하는 WildcardGeneric 객체만 받을 수 있다.

  • 만약 다른 타입으로 선언된 WildcardGeneric 객체를 받으려면 어떻게 해야할까?
  • 가능한 예)

Untitled 5

  • 컴파일 에러가 뜨는 예시)
    • 다른 타입으로 바꾸면 컴파일 에러가 난다. 이유는 제네릭한 클래스의 타입만 바꾼다고 Overloading 이 불가능하기 때문이다.

Untitled 6

  • 위의 컴파일 에러를 고치는 방법은 StringObject 로 타입을 바꾸면된다.
  • String 대신에 ? 적어주면 어떤 타입이 제네릭 타입이 되더라도 상관 없다. 하지만 메소드 내부에서는 해당 타입을 정확히 모르기 때문에 앞서 사용한 것처럼 String으로 값을 받을 수는 없고, Obejct로 처리해야만 한다.

Untitled 7

  • 넘어오는 타입이 다수로 정해져 있다면, instanceof예약어를 사용하여 해당 타입을 확인하면 된다.
public void wildcardStringMethod(WildcardGeneric<?> c){
    Object value = c.getWildcard();
    if(value instanceof String){
        System.out.println(value); //결과 : A, 즉 value는 String 타입이다.
    }
}

wildcardStringMethod() 를 호출한 callWildcardMethod() 에서 아래와 같이 사용하면

public void callWildcardMethod(){
    WildcardGeneric<?> wildcard = new WildcardGeneric<String>();
    wildcard.setWildcard("A");
    wildcardStringMethod(wildcard);
}
  • 아래와 같이 컴파일 에러가 난다.

Untitled 8

  • 알 수 없는 타입에 String을 지정할 수 없다. 다시말해서 어떤 객체를 wildcard로 선언하고, 그 객체의 값을 가져올 수 있지만, 와일드 카드로 객체를 선언했을 때에는 특정 타입으로 값을 지정하는 것을 “불가능”하다.

제네릭 선언에 사용하는 타입의 범위 지정

<> 안에 어떤 타입이 와도 상관이 없다. 하지만 wildcard로 사용하는 타입을 제한할 수 있다.

  • Bounded Wildcards
? extends 타입

예제를 통해서 알아보자.

  • Car 클래스
public class Car{
    protected String name;

    public Car(String name){
        this.name = name;
    }

    public String toString(){
        return "Car name = " + name;
    }
}
  • Car를 상속받은 Bus 클래스
public class Bus extends Car{
    public Bus(String name){
        super(name);
    }

    public String toString(){
        return "Bus name = " + name;
    }
}
  • CarWildcardSample 클래스 → 메소드를 추가하자.
public class CarWildcardSample{
    public static void main(String[] args){
        CarWildSample sample = new CarWildSample();
        sample.callBoundedWildcardMethod();
    }

    public void callBoundedWildcardMethod(){
        WildcardGeneric<Car> wildcard = new WildcardGeneric<Car>();
        wildcard.setWildcard(new Car("Mustang"));
        boundedWildcardMethod(wildcard);
    }

    public void boundedWildcardMethod(WildcardGeneric<? extends Car> c{
        Car value = c.getWildcard();
        System.out.println(value);
    }
}

Untitled 9

boundedWildcardMethod() 메소드에는 “? extends Car” 라고 적어줬다.

이렇게 정의하므로 제네릭 타입으로 Car를 상속받은 모든 클래스를 사용할 수 있다는 의미다.

따라서 boundedWildcardMethod() 의 매개 변수에는 다른 타입을 제네릭 타입으로 선언한 객체가 넘어올 수 없다. 컴파일 에러가 발생한다.

반드시 Car 클래스 와 관련되어 있는 상속한 클래스가 넘어와야만 한다.

public void callBusBoundedWildcardMethod(){
    WildcardGeneric<Bus> wildcard = new WildcardGeneric<Bus>();
    wildcard.setWildcard(new Bus("22"));
    boundedWildcardMethod(wildcard);
}
  • 결과
22

? extends 타입 를 Bounded Wildcards라고 한다. Bound는 경계라는 뜻이다. 매개 변수로 넘어오는 제네릭 타입의 경계를 지정하는 데 사용한다는 의미이다.


메소드를 제네릭하게 선언하기

wildcard로 메소드를 선언하는 방법은 단점이 있다.

  • 매개 변수로 사용된 객체에 값을 추가할 수가 없다는 것이다.

그렇다면 매개 변수로 사용된 객체에 값을 추가할 수 없을까 ?

있다. 예시를 통해서 알아보자

  • 매개 변수로 사용된 객체에 값을 추가하는 방법

Untitled 10

위의 코드는 컴파일은 잘 된다.

메소드 선언시 리턴 타입 앞에 제네릭한 타입을 선언해 주고, 그 타입을 매개 변수에서 사용하면 컴파일할 때 전혀 문제가 없다.

추가로 값도 할당할 수 있다.

  • 실행 → 결과 : Data

Untitled 11

  • ? 를 사용하는 wildcard 처럼 타입을 두리뭉실하게 하는 것보다는 명시적으로 메소드 선언시 타입을 지정해 주면 보다 더 견고한 코드를 작성할 수 있다.
public <T extends Car> void boundedGenericMethod(WildcardGeneric<T> c, T addValue)

이와 같이 선언한다.

제네릭 타입이 여러 개 일때 선언하는 방법

public <S, T extends Car> void multiGenericMethod(WildcardGeneric<T> c, T addValue, S anther))

참고

  • 자바의 신
  • 생활 코딩 : Java - 제네릭(1~ 5)