제네릭(Generic)

Nak Jun Choi·2022년 11월 10일

제네릭 이란?

제네릭의 필요성

  • 사전적으로 '일반적인' 이라는 의미를 갖고있다.
  • 타입을 구체적으로 지정하는 것이 아니라, 추후에 지정할 수 있도록 일반화해두는 것이다.
  • 작성한 클래스 또는 메서드의 코드가 특정 데이터 타입에 얽매이지 않게 해둔 것을 의미한다.

제네릭 클래스

  • 제네릭을 사용한 클래스를 제네릭 클래스라한다.
class Basket<T> {
    private T item;

    public Basket(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}
  • T를 타입매개변수라 한다.
  • 클래스 변수에는 타입 매개변수를 사용할 수 없다.(만약, 클래스 변수에 타입 매개변수를 사용할 수 있다면 클래스 변수의 타입이 인스턴스 별로 달라지게 된다.)
class Basket<T> {
	private T item1; // O 
	static  T item2; // X 
}

제네릭 클래스 사용

  • 타입 매개변수에 치환될 타입으로 기본 타입을 지정할 수 없다. 원시 타입을 지정해야하는 맥락에서는 Integer, Double과 같은 래퍼 클래스를 활용한다.
  • 다형성이 적용된다.
Basket<String>  basket1 = new Basket<String>("Hello");
Basket<Integer> basket2 = new Basket<Integer>(10);
Basket<Double>  basket3 = new Basket<Double>(3.14);

제한된 제네릭 클래스

  • 타입을 지정하는 데에 있어 제한이 없다.
  • 하지만
class Basket<T extends Flower> {
    private T item;
	
		...
}
  • 위와 같이 코드를 작성하게 되면, 타입의 종류가 Flower의 하위 클래스로만 제한되게 된다.

  • 또한 특정 클래스를 상속받은 클래스만 타입으로 지정할 수 있도록 제한하는 것뿐만 아니라, 특정 인터페이스를 구현한 클래스만 타입으로 지정할 수 있도록 제한할 수도 있다.

  • 만약, 특정 클래스를 상속받으면서 동시에 특정 인터페이스를 구현한 클래스만 타입으로 지정할 수 있도록 제한하려면 아래와 같이 &를 사용하면 된다.(주의할 점은 클래스를 인터페이스보다 앞에 위치시켜야 한다.)

class Basket<T extends Flower & Plant> { // (1)
    private T item;
	
		...
}

제네릭 메서드

  • 클래스 내부의 특정 메서드만 제네릭으로 선언할 수 있는데, 이를 제네릭 메서드라 한다.
class Basket {
		
		public <T> void add(T element) {
				
		}
}
  • 제네릭 메서드의 타입 매개변수는 제네릭 클래스의 타입 매개변수와 별개의 것이다.이유는 타입이 지정되는 시점이 서로 다르기 때문이다. 즉, 클래스 명 앞에서 선언한 타입 매개변수는 클래스가 인스턴스화될 때 타입이 지정되고, 제네릭 메서드의 타입지정은 메서드가 호출될 때 이루어진다.
class Basket<T> {                        // 1 : 여기에서 선언한 타입 매개변수 T와 
		
		public <T> void add(T element) { // 2 : 여기에서 선언한 타입 매개변수 T는 서로 다른 것이다.
				
		}
}
  • 메서드 타입 매개변수는 클래스 타입 매개변수와 달리 static 메서드에서도 선언하여 사용할 수 있다.
  • 모든 클래스는 `object'클래스를 상송받기 때문에 object 클래스의 메소드는 사용이 가능하다.

와일드카드

  • 자바의 제네릭에서 와일드 카드는 어떠한 타입으로든 대체될 수 있는 타입 파라미터를 의미하며, ? 키워드로 사용할 수 있다.
<? extends T>
<? super T>
  • <? extend T>는 와일드 카드에 상한 제한을 두는 것으로, TT를 상속받는 하위 클래스 타입만 타입 파라미터로 받을 수 있도록 지정한다.
  • <? super T>는 와일드카드에 하한 제한을 두는 것으로, TT의 상위 클래스만 타입 파라미터로 받도록 한다.
  • 참고로 <?><? extends Object>와 같다. 모든 클래스 타입은 Object 클래스를 상속받으므로, 모든 클래스 타입을 타입 파라미터로 받을 수 있음을 의미한다.
public class Example {
    public static void main(String[] args) {
        PhoneFunction.call(new User<Phone>(new Phone()));
        PhoneFunction.call(new User<IPhone>(new IPhone()));
        PhoneFunction.call(new User<Galaxy>(new Galaxy()));
        PhoneFunction.call(new User<IPhone12Pro>(new IPhone12Pro()));
        PhoneFunction.call(new User<IPhoneXS>(new IPhoneXS()));
        PhoneFunction.call(new User<S22>(new S22()));
        PhoneFunction.call(new User<ZFlip3>(new ZFlip3()));

        System.out.println("\n######################################\n");

//        PhoneFunction.faceId(new User<Phone>(new Phone())); // X
        PhoneFunction.faceId(new User<IPhone>(new IPhone()));
        PhoneFunction.faceId(new User<IPhone12Pro>(new IPhone12Pro()));
        PhoneFunction.faceId(new User<IPhoneXS>(new IPhoneXS()));
//        PhoneFunction.faceId(new User<Galaxy>(new Galaxy())); // X
//        PhoneFunction.faceId(new User<S22>(new S22())); // X
//        PhoneFunction.faceId(new User<ZFlip3>(new ZFlip3())); // X

        System.out.println("\n######################################\n");

//        PhoneFunction.samsungPay(new User<Phone>(new Phone())); // X
//        PhoneFunction.samsungPay(new User<IPhone>(new IPhone())); // X
//        PhoneFunction.samsungPay(new User<IPhone12Pro>(new IPhone12Pro())); // X
//        PhoneFunction.samsungPay(new User<IPhoneXS>(new IPhoneXS())); // X
        PhoneFunction.samsungPay(new User<Galaxy>(new Galaxy()));
        PhoneFunction.samsungPay(new User<S22>(new S22()));
        PhoneFunction.samsungPay(new User<ZFlip3>(new ZFlip3()));

        System.out.println("\n######################################\n");

        PhoneFunction.recordVoice(new User<Phone>(new Phone()));
//        PhoneFunction.recordVoice(new User<IPhone>(new IPhone())); // X
//        PhoneFunction.recordVoice(new User<IPhone12Pro>(new IPhone12Pro())); // X
//        PhoneFunction.recordVoice(new User<IPhoneXS>(new IPhoneXS())); // X
        PhoneFunction.recordVoice(new User<Galaxy>(new Galaxy()));
//        PhoneFunction.recordVoice(new User<S22>(new S22())); // X
//        PhoneFunction.recordVoice(new User<ZFlip3>(new ZFlip3())); // X
    }
}

profile
HelloWorld

0개의 댓글