코드스테이츠_국비교육/[Section1]

16.02_제네릭 클래스_22.09.13

생각없이 해도 생각보다 좋다. 2022. 9. 13. 22:31

학습키워드

  • 제네릭
  • 제네릭 클래스
  • 제네릭 메서드
  • Wrapper 클래스

 

제네릭

>의미

: 타입을 구체적으로 지정하지 않고, 추후에 지정할 수 있도록 '일반화'해두는 것.

>특징 및 장점

: 작성한 클래스, 메서드가 특정 데이터 타입에 얽매이지 않게 설정할 수 있음.

: 유연한 코드. 즉, 확장성이 넓어짐.

 

제네릭 클래스

>의미 및 특징

: 제네릭이 사용된 클래스

: 제네릭 클래스도 상속 및 다형성을 적용할 수 있다.

: 인스턴스 생성 시, 원하는 타입을 인자로 줄 수 있다.

>제네릭 클래스 문법 구조

public class Main {
    public static void main(String[] args) {
        //타입 인자: String, T = String
        Basket<String> basket1 = new Basket<String>("Hello");
        //타입 인자: Integer(Wrapper), T = Integer
        Basket<Integer> basket2 = new Basket<Integer>(10);
        //타입 인자: Double(Wrapper), T = Double
        Basket<Double>  basket3 = new Basket<Double>(3.14);
        //Basket<Double>  basket3 = new Basket<>(3.14);
        //좌항(참조형)의 꺽쇠에 타입 인자를 제공하면,
        //우항(생성자)의 꺽쇠(타입 인자)는 생략 가능
    }
}
//클래스명 옆 꺽쇠 및 타입 매개변수로 제네릭 클래스임을 표시.(T)
class Basket<T> {
    //타입 인자를 받을 곳을 타입 매개변수로 표시.(T)
    private T item;
    //생성자
    public Basket(T item) {
        this.item = item;
    }
}

>타입 매개변수

: 인스턴스 생성시 인자로 받을 자료형이 사용될 곳을 표현함.

: 해당 클래스가 제네릭 클래스임을 표현함.

: 여러 개의 타입 매개변수는 쉼표(,)로 구분함.

: 제네릭 클래스임을 표현할 땐, 제네릭 클래스명 옆에 위치하며, 꺽쇠<>로 타입 매개변수를 감싸서 표현함.

: 타입 매개변수는 임의의 문자를 사용해도 되지만, 관례적으로 사용하는 것들이 존재함.

/*예시*/
<T> Type
<E> Element
<K> Key
<V> Value
<N> Number

>타입 매개변수 조건

  • 클래스 변수 혹은 클래스 메서드에는 타입 매개변수를 사용할 수 없음

: Static 멤버는 클래스 생성 시 저장되며, 인스턴스 생성에 영향을 받지 않기 때문.

  • 타입 매개변수에 제공받는 인자로는 기본 자료형을 쓸 수 없다.

: 기본 자료형을 사용해야 하는 경우에는 Wrapper 클래스를 이용한다.

>제네릭 클래스의 객체화

: 인스턴스 생성시 참조타입(클래스명), 그리고 생성자명 옆에 꺽쇠<>로 자료형을 인자로 제공한다.

: 참조타입에서 인자로 자료형을 제공한 경우, 생성자명의 꺽쇠<>에는 자료형 인자를 생략해도 된다.

>제한된 제네릭 클래스

: 타입 매개변수에 받을 인자의 범위를 제한하는 방법.

: extends 키워드를 사용. (인터페이스 구현의 제한도 extends 키워드 사용)

: 상속 관계에서의 하위 클래스, 혹은 인터페이스를 구현한 클래스만 타입으로 지정할 때 사용한다.

: 상속받는 클래스와 구현하는 인터페이스를 동시에 제한하고 싶을 경우에는 엠퍼센트(&)로 구분한다.

public class Main {
    public static void main(String[] args) {
        //타입 인자: Cake, 타입 인자 제한 조건: X
        GiftBox<Cake> cake = new GiftBox<>();//가능
        GiftBox<Pizza> pizza = new GiftBox<>();//가능
        
        //타입 인자 제한 조건: Cake, 혹은 그 하위 클래스
        CakeBox<Cake> cake1 = new CakeBox<>();//가능
        CakeBox<CheeseCake> cake2 = new CakeBox<>();//가능
        CakeBox<Pizza> cake3 = new CakeBox<>(); //에러
        
        //타입 인자 제한 조건
        //:Cake의 하위 클래스이면서 동시에 Strawberry를 구현하는 클래스
        CakeBox2<Cake> cake4 = new CakeBox2<>(); //에러
        CakeBox2<CheeseCake> cake5 = new CakeBox2<>(); //에러
        CakeBox2<Strawberry> cake6 = new CakeBox2<>(); //에러
        CakeBox2<StrawberryCheeseCake> cake7 = new CakeBox2<>();//가능
        
        //제네릭 클래스의 다형성
        GiftBox<Cake> cake9 = new GiftBox<>();
        cake9.someMethod(new CheeseCake()); //가정: someMethod
        //Cake의 참조형으로 생성한 CheeseCake 인스턴스를 인자로 제공할 수 있음.
    }
}
// 다양한 클래스(상속, 구현)
class Cake {  }
class CheeseCake extends Cake {  }
class Pizza {  }
interface Strawberry {  }
class StrawberryCheeseCake extends Cake implements Strawberry {  }

// 제네릭 클래스 정의
class GiftBox<T> {  }
class CakeBox<T extends Cake> {  }
class CakeBox2<T extends Cake & Strawberry> {  }