Generics

허진혁·2023년 7월 11일
0

기본기를 다지자

목록 보기
4/10

🤔 궁금사항

제네릭이란 무엇이며, 왜 사용할까요?

또한 제네릭을 사용하여 어떤 이점을 얻을 수 있나요?

Generic

제네릭은 클래스, 인터페이스 및 메서드를 정의할 때, 실제 사용될 타입을 타입 파라미터로 받아 사용하는 방식이에요. 관례적으로 타입파라미터는 Type의 T와 Element의 E 등을 사용해요.

public class ResponseEntity<T> extends HttpEntity<T>
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

타입 파라미터는 제네릭에서 사용되는 변수처럼 동작하는 것으로 클래스(인터페이스)가 생성될 때, 메서드가 호출될 때 실제 타입으로 대체되요.

✨ Generic 역할과 이점

타입 안정성 보장

제네릭은 컴파일시에 타입을 확인하기에 잘못된 타입으로 인한 런타임 오류를 방지해요. 즉, 컴파일 시점에서 타입의 오류를 알기 때문에 개발자가 쉽게 확인할 수 있어요.

재사용성

제네릭을 사용하면 다양한 타입에 대해 동일한 클래스나 메서드를 재사용할 수 있어요. 타입에 따라 새로운 클래스를 정의할 필요 없이 제네릭을 활용하여 타입 파라미터에 각각의 타입을 넣어 사용할 수 있어요.

이는 오버로딩과 비슷한 느낌이에요. 파라미터의 개수나 타입에 따라 같은 메서드명을 사용할 수 있는 것처럼 같은 클래스(인터페이스)명이지만 타입에 따라 새로 만드는 것이 아니라 안에 알맞은 타입을 넣고 사용할 수 있어요.

가독성

타입 파라미터를 사용하여 코드의 의도를 명확히 파악할 수 있고, 굳이 형변환을 하지 않아도 돼요.

컬렉션의 타입 안정성

제네릭을 사용하면 컬렉션 클래스를 타입 안정성 있게 사용하도록 도와주어요. 컴파일러가 타입 체크를 수행하여 잘못된 타입의 요소가 컬렉션에 추가되는 것을 방지할 수 있어요.

Ⓣ 와일드카드(wildcard) 타입

제네릭을 사용할 때 <>안에 들어가는 타입은 어떤 타입이라도 상관이 없어요.

메소드의 매개 변수로 넘어갈 때 제네릭에 사용 되는 ‘?’를 와일드 카드 타입이라고 해요.

와일드 카드로 선언한 후에 객체의 값을 가져올 수는 있지만, 특정 타입으로 값을 지정하는 것은 불가능해요.

WildCardGeneric

public class WildCardGeneric<W> {
    W wildCard;
    public void setWildCard(W wildCard) {
        this.wildCard = wildCard;
    }
    
    public W getWildCard() {
        return wildCard;
    }
}

WildCardMain

public class WildCardMain {
    public static void main(String[] args) {
        WildCardMain wildCardMain = new WildCardMain();
        wildCardMain.callWildCardGeneric();
    }

    public void callWildCardGeneric() {
        WildCardGeneric<?> wildCardGeneric = new WildCardGeneric<>();
        wildCardGeneric.setWildCard("hello");
        wildCardStringMethod(wildCardGeneric);
    }

		// 메소드에서 제네릭 와일드카드 사용
    public void wildCardStringMethod(WildCardGeneric<?> c) {
        Object value = c.getWildCard();
        System.out.println(value);
    }
}

와일드카드는 메소드의 매개 변수로 사용하는 것이 좋아요.

위와 같이 설계했을 때 다음과 같은 컴파일 오류를 찾을 수 있어요.

제네릭 선언에 사용하는 타입의 범위 지정하기

제네릭을 사용할 때 <> 안에는 어떠한 타입이라도 상관 없다고 했지만 와일드카드 처럼 타입을 애매하게 하는 것보다는 명시적으로 타입을 지정해 주면 코드의 예측가능성을 올릴 수 있어요.

범위를 제한하는 방식은 두 가지 방식이 있어요.

<? extends 상위타입>

해당 와일드 카드는 상위타입이거나 상위타입의 하위타입으로 제한하는 방식이에요.

데이터를 리턴하는 메소드(Producer)에서는 ? extends T를 사용해요.

읽기 작업(상위 타입의 요소를 가져오는 작업)에 사용돼요.

<? super 하위타입>

해당 와일드 카드는 하위타입 이거나 하위타입의 상위타입으로 제한하는 방식이에요.

쓰기 작업(하위타입으로 요소를 추가하는 작업)에 사용돼요.

❗️ 와일드카드 사용시 고려해야할 사항

PECS(Producer Extends, Consumer Super) 원칙

제네릭에서 와일드카드 타입을 사용할 때 권장되는 설계 원칙이에요. 와일드카드 타입이 매개변수로 사용되는 경우 데이터를 생산(리턴)하는 메소드에서는 extends를 사용하고, 데이터를 소비(파라미터로 받음)하는 메소드에서는 super를 사용하는 것을 의미해요.

  • Producer: 데이터를 생산(리턴)하는 역할을 하는 메소드 또는 클래스입니다. 주로 읽기 작업에 해당합니다.
  • Extends: 와일드카드 타입을 사용하여 상위 타입의 데이터를 생산할 수 있게 합니다.
  • Consumer: 데이터를 소비(파라미터로 받음)하는 역할을 하는 메소드 또는 클래스입니다. 주로 쓰기 작업에 해당합니다.
  • Super: 와일드카드 타입을 사용하여 하위 타입의 데이터를 소비할 수 있게 합니다.

PECS 원칙을 적용하면, 제네릭 타입의 유연성을 높일 수 있고, 타입 안정성을 유지하면서 다양한 타입의 데이터를 처리할 수 있게 돼요. 이를 통해 제네릭 코드의 재사용성과 확장성을 향상시킬 수 있어요.

CQS(Comand-Query Separation Principle) 위반을 피해라

소프트웨어 설계 원칙 중 하나로, 한 메서드는 command와 query를 동시에 처리하지 말아야 한다는 원칙이에요.

  • command: 어떤 동작을 수행하고 결과를 반영하는 작업
  • query: 어떤 정보를 반환하는 작업

CQS를 따르는 코드에서는 명령과 질의를 수행하는 메서드가 분리되어야 해요.

명령을 처리하는 메서드는 어떤 작업을 수행하고 객체 상태를 변경하며, 반환값이 없어야 해요.

반면, 질의를 처리하는 메서드는 객체의 상태를 변경하지 않고 필요한 정보를 반환해야 해요. 이렇게 분리된 메서드는 코드를 이해하기 쉽게 만들고, 응집력 있는 클래스를 설계를 할 수 있어요.

CQS를 따르지 않으면 다음과 같은 문제를 야기해요.

  1. 의도 파악의 어려움: 명령과 질의가 함께 존재하는 메서드는 코드의 의도를 파악하기 어려워요. 메서드가 동시에 객체 상태를 변경하고 정보를 반환한다면, 두 가지 책임을 동시에 지니게 되고, 이를 통해 어떤 역할을 수행하는지 명확하게 이해하기 어려워요. 즉, 코드 가독성이 떨어지게 되요.
  2. 부작용 발생 가능성: 명령과 질의가 혼합된 메서드는 부작용(side effect)이 발생할 가능성이 높아져요. 부작용은 예상치 못한 결과를 초래하고 코드의 예측 가능성을 낮추어요.
  3. 코드 유지보수의 어려움: 명령과 질의가 혼합된 메서드는 변경 시점에 주의가 필요해져요. 만약 해당 메서드를 수정하면 객체 상태 변경과 관련된 다른 코드도 함께 변경될 가능성이 존재해요. 이는 코드의 의도를 파악하기 어렵게 만들고 버그를 발생시킬 수 있어요.

CQS을 지키면 코드의 의도를 명확하게 드러내며(코드 가독성 증가), 부작용(side effect)이 최소화되고 예측 가능한 동작을 가진 코드를 작성할 수 있게 되요. 이를 통해 코드의 가독성과 유지보수성이 향상되며, 버그 발생 가능성을 줄일 수 있어요.

참고자료

자바의 신
Java Generics PECS – Producer Extends Consumer Super

profile
Don't ever say it's over if I'm breathing

0개의 댓글