본문 바로가기
Language/JAVA

Generics (지네릭스) 를 알아보자

by YoonJong 2023. 2. 12.
728x90

Generics는 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입체크를 해주는 기능입니다.

장점으로는, 컴파일 시에 체크하기 때문에, 문법적인 오류를 바로 확인할 수 있고, 타입의 안정성을 높일 수 있습니다.

또한, 다룰(사용할) 객체 타입을 미리 명시함으로써, 형변환의 번거로움을 줄여줍니다.

 

지네릭는 아래와 같이 선언되어있습니다.

 

지네릭을 사용하지 않는 객체의 타입은 아래와 같습니다.

class Box {
    Object item;

    public Object getItem() {
        return item;
    }

    public void setItem(Object item) {
        this.item = item;
    }
}

 

Box<T> 으로 지네릭클래스를 설정할 수 있습니다. T는 타입 변수를 뜻하며, 원하는 타입을 적용할 수 있습니다.

ArrayList<E> 의 경우에는 E = Element(요소)를 뜻하며, Map<K,V> 같은 경우에는 K=key , V=Value 를 뜻합니다.

( 기호의 종류만 다르고 참조형 타입을 사용한다는 의미는 같습니다. )

 

기본적으로 T , E , K , V 를 사용해도 상관없으나, 상황에 맞는 의미있는 타입을 작성해주는 것이 효율적으로 사용하는 방법입니다.

 

지네릭 타입을 이용해서 설정하면 아래와 같습니다.

String 타입만 사용할 수 있는 Box 에 set 을 이용해서 값을 설정해보면, String 이외의 타입은 에러가 발생하는 것을 볼 수 있습니다.

class Box<String> {
    String item;

    public String getItem() {
        return item;
    }

    public void setItem(String item) {
        this.item = item;
    }
    ...
    
    Box<String> box = new Box<>();
        
    box.setItem(new Object()); // 타입오류로 에러발생
    box.setItem("박스셋팅");
}

 

지네릭스는 한계와 제한을 가지고 있습니다.

지네릭스는 static 멤버의 타입변수는 사용할 수 없습니다.

이유는 static은 모든 객체에 대해 동일하게 작동해야하므로, 멤버는 인스턴스변수를 참조하는 것은 불가능하기 때문입니다.

추가적으로, new 연산자 때문에 지네릭배열을 생성할 수 없습니다.

 

지네릭 클래스의 객체 생성은 타입이 같아야 가능합니다.

Box<Apple> appleBox = new Box<Apple>; // 가능
Box<Apple> appleBox = new Box<>; // 가능 (생략O)
Box<Apple> appleBox = new Box<peach>; // 에러 (타입불일치)

 

타입 문자로 사용할 타입을 명시하면 한 종류의 타입만 저장할 수 있지만, 모든 종류의 타입을 지정할 수 있는 문제가 있다.

FruitBox<Toy> fruitBox = new FruitBox<Toy>();
fruitBox.add(new Toy()); // 과일 상자에 장난감을 담을 수 있다.

이를 해결하기 위해서는 extends 를 이용해 특정 타입의 자식들만 대입할 수 있도록 한다.

class FruitBox<T extends Fruit> {...} // Fruit 의 자식만 담을 수 있다.

Fruit 뿐만 아니라 추가적인 인터페이스를 구현해야 한다면 아래와 같이 & 를 사용해 추가할 수 있다.

class FruitBox<T extends Fruit & Eatable> {...} // Fruit 의 자식과 Eatable 를 구현한 클래스를 담을 수 있다.

 

 

일반 클래스에서는 static 을 사용해서 매개변수로 지네릭을 사용할 수 있습니다.

class Juicer {
	static Juice makeJuice(FruitBox<Fruit> box) { }

위와 같이 매개변수에 Fruit 로 명시해주면, 다른 FruitBox<Apple> 은 사용하지 못합니다.

아래처럼 오버로딩으로 처리할 수도 없습니다. 지네릭은 타입이 다른 것만으로는 오버로딩이 성립되지 않습니다.

class Juicer {
static Juice makeJuice(FruitBox<Fruit> box) { }
static Juice makeJuice(FruitBox<apple> box) { } // 에러 발생

 

이를 해결하기 위해서는 와일드 카드를 사용해야 합니다.

와일드 카드는 기호 "?" 표시로 표현합니다.

<?> 제한 없음. <? extends Object> 와 동일하다
<? extends T> T와 그 자식들만 가능
<? super T> T와 그 부모들만 가능

 

와일드 카드를 사용하면 아래와 같이 변경할 수 있습니다.

class Juicer {
	static Juice makeJuice(FruitBox<? extends Fruit> box) { }

 


참고 : 자바의 정석 3판 

728x90

'Language > JAVA' 카테고리의 다른 글

람다식과 함수형 인터페이스  (0) 2023.02.13
쓰레드 동기화(Synchronization)  (0) 2023.02.13
Thread.sleep() 과 interrupted()  (0) 2023.02.12
쓰레드(Thread) 는 무엇일까?  (0) 2023.02.12
열거형(Enum) 을 알아보자  (0) 2023.02.12

댓글