본문 바로가기

Programming Language/Java

[Java] 제네릭(Generic) 분석

T extends 클래스

  • 상속을 이용해서 T의 자료형을 제한함
  • 클래스 선언시 사용하며 인스턴스 생성시 특정 클래스를 상속받은 클래스형만
    인스턴스 내부에서 사용할 수 있도록 함
  • 특정 인터페이스를 구현한 클래스만 사용하려는 경우에도 사용 가능
public interface Behavior {

    void displayName();
}

 

public class Car implements Behavior{

    @Override
    public void displayName() {
        System.out.println("This is a car.");
    }
}

 

public class Bus implements Behavior{

    @Override
    public void displayName() {
        System.out.println("This is a bus.");
    }
}

 

// T, K 표기 시 extends로 들어갈수 있는 class 범위를 지정할 수 있다. 여기서는 Behavior 타입의 자손들만 올 수 있다.
public class GenericExample<T extends Behavior, K extends Behavior> implements Behavior {

    private T vehicle1;
    private K vehicle2;

    void injectVehicle(T vehicle1, K vehicle2) {
        this.vehicle1 = vehicle1;
        this.vehicle2 = vehicle2;
    }

    public void displayName() {
        vehicle1.displayName();
        vehicle2.displayName();
    }

    T getFirst() {
        return vehicle1;
    }

    //static 메소드의 경우 메모리에 객체생성전에 올라가므로 타입 선언을 따로 독자적으로 한다. 
    //제일 앞에 <>로 표시한다.
    //class에서 정의한 T,K와는 다른 E로 정의가능하다.
    
    static <E> E getSecond(E o) {
        return o;
    }
    
}

 

public class Main {

    public static void main(String[] args) {
        GenericExample<Car, Bus> ge = new GenericExample<>();
        ge.injectVehicle(new Car(), new Bus());
        ge.displayName();

        ge.getFirst().displayName();
        
        //getSecond 메소드의 경우 따로 타입이 E형으로 지정되어 있다.
        ge.getSecond(new Car()).displayName();
    }
}

 

실행결과는 다음과 같다

 

This is a car.
This is a bus.
This is a car.
This is a car.

 

< ? extends 클래스 >

  • 매개변수의 자료형을 특정 클래스를 상속받은 클래스로만 제한함

< ? super 클래스 >

  • 매개변수의 자료형을 특정 클래스와 그 클래스의 상위 클래스로만 제한함
  • ※ super는 T에 사용 불가

< ? >

  • 메소드 매개변수의 자료형에 사용되는 제너릭
  • < ? extends Object > 의 줄임 표현
  • 어떤 자료형의 객체도 매개변수로 받겠다는 의미
  • Unbounded WildCard라고 알려져 있음
public class GenericExample<T extends Behavior, K extends Behavior> implements Behavior {

    private T vehicle1;
    private K vehicle2;

    void injectVehicle(T vehicle1, K vehicle2) {
        this.vehicle1 = vehicle1;
        this.vehicle2 = vehicle2;
    }

	// 어떤 타입의 클래스도 다 들어옴
    public void displayNameWild(List<?> vehicleList) {
        for (Object obj : vehicleList) {
            System.out.print(obj);
        }
    }

	// Vehicle 을 상속한 클래스, 즉 Vehicle 포함 모든 자식 클래스 가능
    public void displayNameExtendWild(List<? extends Vehicle> vehicleList) {
        for (Vehicle obj : vehicleList) {
            obj.displayName();
        }
    }

	// Bus의 모든 부모 클래스 가능
    public void displayNameSuper(List<? super Bus> vehicleList) {
        for (Object obj : vehicleList) {
            System.out.print(obj);
        }
    }