조종 다음은 개발
article thumbnail

⚽️ 제네릭(Generic) 이란?

제네릭이란 데이터 형식의 의존하지 않고 하나의 값이 여러 타입을 가질 수 있도록 하는 방법이다.

어떤 타입의 데이터를 사용할지는 외부에서 지정해 주면 된다.

 

이게 뭔소리야..? 라고 생각된다면 아래 예시를 보자.

List<Integer> list = new ArrayList<>();

자바에서 매우 자주 사용되는 리스트 컬렉션이다. 여기서 저 꺾쇠괄호 <> 가 바로 제네릭이다.
저 괄호 안에 사용할 타입을 지정해 주는 것이다.

 

리스트에서는 List<String> 도 가능하고 위 예시처럼 List<Integer> 도 가능하다.


즉, 데이터 형식에 의존하지 않고 여러 타입을 가질 수 있는 것이다.


이를 제네릭이라고 한다.

실제로 List 선언된 곳을 보면 위와 같이 제네릭을 활용한다는 것을 알 수 있다.

⚖️ 타입 파라미터 정의

그러면 실제로 어떤 식으로 정의해서 사용하는지 살펴보자.

위 사진을 보면 클래스 선언 시 public class Box <T> 와 같이 선언한 것을 볼 수 있다.
그러면 해당 클래스 내부에서 T 타입을 사용해 줄 수 있다.

클래스 내부에서는 어떤 타입이 올지는 신경 쓰지 않고 기능을 구현한다.

 

T에 어떤 타입을 올지는 외부에서 선언해 준다.

 

꼭 하나의 제네릭 타입만 사용해야 하는 것은 아니다. 여러개의 제네릭 타입을 사용할 수 있다.


또한, 이름도 T 만 사용해야하는 것이 아니다.

어느 정도 규칙은 있지만 본인 마음대로 이름을 선언해서 사용할 수 있다.

📢 할당 가능한 타입

제네릭으로 할당 가능한 타입들은 참조 타입들 뿐이다.
즉, int 형이나 double 형 같이 원시타입들은 제네릭 타입으로 지정할 수 없다.

 

List 에서도 원시타입으로 선언하면 에러가 발생하는 것을 한 번씩 본 적 있을 것이다.

실제로 List<int> 와 같이 원시타입을 지정해 보면 에러가 발생하는 것을 볼 수 있다.

 

따라서 제네릭 타입으로는 참조 타입인 String, Integer , Double 등을 사용해야 한다.

📛 타입 파라미터 네이밍

앞서, 제네릭 타입의 이름을 마음대로 지정해 줄 수 있다고 했었다.
그런데 통상적으로 사용하는 네이밍 규칙들이 있다.

타입 설명
<T> Type
<E> Element
<K> Key
<V> Value
<N> Number

Element는 List 등과 같은 요소들을 말한다.

Key, Value는 Map <K, V>와 같이 키, 값 형태의 타입을 말한다.

 

반드시 지켜야하는 규칙은 아니지만 네이밍 규칙을 지킨다면 다른 사람들이 내 코드를 읽었을 때 가독성이 향상될 수 있다.

따라서 지키는게 좋을 것 같다.

🧐 그렇다면 제네릭의 장점은 무엇일까?

그렇다면 왜 제네릭을 쓸까?? 어떤 장점이 있을까??

 

1. 제네릭을 사용하면 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지할 수 있다.

이게 무슨 장점인지 이해하기 위해서는 제네릭 사용 이전에 대해 알아야 한다.


제네릭은 java 1.5부터 추가된 스펙이다.
그래서 java 1.5 이전에는 제네릭 대신 모든 객체의 최상위 객체인 Object를 사용했다.

위와 같이 모든 타입을 받을 수 있는 Object 타입으로 선언해서 사용했다.

 

그리고 이후 외부에서 원하는 타입으로 변환해 주어서 사용했다.

 

만약 이때 잘못된 타입을 넣는 경우 어떨까??

우리는 두 번째 값으로 Integer 타입을 기대하고 코드를 작성했었다.
그런데 잘못해서 두 번째 타입으로 Integer 타입이 아닌 String 타입의 값을 넣어주었다.
그러나 위 사진에서 볼 수 있듯이 컴파일 단계에서는 어떠한 에러도 발생하지 않는다.

String 타입을 Integer녛 타입으로 변환할 수 없다는 에러가 발생한다.

코드를 실행하고 나서야 런타임 에러가 발생하여 문제가 있다는 것을 확인할 수 있다.

 

코드가 복잡해질 수 록 이러한 디버깅에는 너무 많은 노력이 필요된다.

그런데 제네릭을 사용하면 코드를 실행하기 전부터 빨간 줄로 문제가 있다는 것을 알려준다.

즉, 제네릭을 사용하면 코드를 작성하는 단계에서 문제점을 파악하고 빠르게 수정해 줄 수 있는 것이다.

 

2. 클래스 외부에서 타입을 지정해 주기 때문에 따로 타입을 체크하고 변환해 줄 필요가 없다. 

제네릭을 사용한다면 Object를 사용했을 때 같이 (String) box.getItamA() 처럼 타입을 캐스팅 해줄 필요가 없다.
물론 타입을 체크해 줄 필요도 없다. 제네릭을 사용한다면 컴파일 단계에서 다 확인해 주기 때문이다.

3.  코드의 재사용성이 높아진다.

제네릭을 사용하게 된다면 List 를 문자열에 대한 리스트, 숫자형에 대한 리스트를 각각 정의해주지 않는 것처럼
하나의 선언으로 여러 타입의 코드를 사용할 수 있다.

즉, 코드의 재사용성이 높아지는 것이다.

 

🧑‍💻 제네릭 메서드

제네릭을 꼭 클래스 단위에서만 사용하는 것은 아니다.

위 사진과 같이 메서드 단위에서도 제네릭을 활용할 수 있다.

조금 헷갈릴 수 있는데 간단하게 생각하면 된다.

제네릭을 사용하고 싶다면 앞에 제네릭을 선언해 주고 메서드에서 사용해 주면 되는 것이다.

해당 제네릭 메서드를 사용한다면 위와 같이 사용할 수 있다.

 

만약 public , private final와 같은 예약어가 있다면 예약어 뒤에 제네릭을 선언해 줘야 한다.

public <T> t someMethod(T t) {
    ...
    return t;
}

위 코드와 같이 말이다.

profile

조종 다음은 개발

@타칸

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!