제네릭(Generic)

김준영·2023년 3월 6일
1

Code States

목록 보기
6/33

제네릭이란?


작성한 클래스 또는 메서드의 코드가 특정 데이터 타입에 얽매이지 않게 해둔 것

사용법


제네릭 클래스

class Basket<T> {
    private T item;

    ...
}


// 타입 매개변수 여러 개 사용 시
class Basket<K, V> { ... }
class Basket<T> {
	private T item1; // O 
	static  T item2; // X 
}

클래스 변수에는 타입 매개 변수를 사용할 수 없다.
클래스 변수에 타입 매개변수를 사용할 수 없는 이유는 클래스 변수의 특성을 생각해보면 충분히 이해할 수 있습니다. 클래스 변수는 모든 인스턴스가 공유하는 변수입니다. 만약, 클래스 변수에 타입 매개변수를 사용할 수 있다면 클래스 변수의 타입이 인스턴스 별로 달라지게 됩니다.

class Flower { ... }
class Rose extends Flower { ... }
class RosePasta { ... }

class Basket<T extends Flower> {
    private T item;
	
		...
}

class Main {
    public static void main(String[] args) {
    
        // 인스턴스화 
        Basket<Rose> roseBasket = new Basket<>();
        Basket<RosePasta> rosePastaBasket = new Basket<>(); // 에러
    }
}

Basket 클래스를 인스턴스화 할때 타입으로 Flower 클래스의 하위 클래스만 지정하도록 제한된다.

interface Plant { ... }
class Flower implements Plant { ... }
class Rose extends Flower implements Plant { ... }

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

class Main {
    public static void main(String[] args) {

        // 인스턴스화 
        Basket<Flower> flowerBasket = new Basket<>();
        Basket<Rose> roseBasket = new Basket<>();
    }
}

만약, 특정 클래스를 상속받으면서 동시에 특정 인터페이스를 구현한 클래스만 타입으로 지정할 수 있도록 제한하려면 아래와 같이 &를 사용하여 코드를 작성해주면 됩니다.

다만, 이러한 경우에는 클래스를 인터페이스보다 앞에 위치시켜야 합니다. 아래 예제의 (1)을 참고하세요.

제네릭 메서드

class Basket {
		...
		public <T> void add(T element) {
				...
		}
}

class Basket<T> {                        // 1 : 여기에서 선언한 타입 매개변수 T와 
		...
		public <T> void add(T element) { // 2 : 여기에서 선언한 타입 매개변수 T는 서로 다른 것입니다.
				...
		}
}

제네릭 메서드의 타입 매개변수는 제네릭 클래스의 타입 매개변수와 별개의 것이다.

이는 타입이 지정되는 시점이 서로 다르기 때문이다.

즉, 클래스명 옆에서 선언한 타입 매개변수는 클래스가 인스턴스화될때 타입이 지정된다.

그러나, 제네릭 메서드의 타입 지정은 메서드가 호출될 때 이루어진다.

또한, 클래스 타입 매개변수와 달리 메서드 타입 매개변수는 static 메서드에서도 선언하여 사용할 수 있다.

class Basket {
		...
		static <T> int setPrice(T element) {
				...
		}
}

제네릭 메서드를 정의하는 시점에서는 실제 어떤 타입이 입력되는지 알 수 없다.
따라서 length()와 같은 String 클래스의 메서드는 제네릭 메서드를 정의하는 시점에 사용할 수 없다.

class Basket {
    public <T> void print(T item) {
        System.out.println(item.length()); // 불가
    }
}

하지만 최상위 클래스인 Object 클래스의 메서드는 사용가능.

class Basket {
    public <T> void getPrint(T item) {
        System.out.println(item.equals("Kim coding")); // 가능
    }
}

와일드 카드


어떠한 타입으로든 대체될 수 있는 타입 파라미터를 의미

// T와 T를 상속받는 하위 클래스 타입만 타입 파라미터로 받을 수 있다.
<? extends T>

// T와 T의 상위 클래스만 타입 파라미터로 받도록 한다.
<? super T>
profile
ㅎㅎ

0개의 댓글