Java의 Generic

jiho·2021년 5월 27일
0

Java

목록 보기
6/6

이번 정리 글은 Generic이 뭔지는 알지만 왜쓰는지를 더 정확히 정리하는데에 목적을 둡니다.

다양한 타입을 수용할 수 있는 자료구조를 만들기 위해? 다양한 타입을 지원하는 Class를 구현하기 위해.
타입 형변환의 에러를 피하기위해 등 여러 목적에 있습니다.

자바는 여러 타입들이 존재하기 때문에, 형변환(casting)을 하면서 많은 예외가 발생할 수 있다.


import java.util.List

class TestProgram {
  public static void main(String[] args) {
    List<Object> items = new List<>();
    items.add(new Item1());
    items.add(new Item2());
  }
}

다음과 같이 부모 클래스 or 인터페이스 타입으로 정의된 List가 있을 경우, 값을 넣을 때는 상관없지만 꺼내서 알맞은 클래스 형태로 얻고 싶다면 명시적인 형변환(casting)이 필요합니다.


Item2 item = (Item2)items.get(1); // Item1 object를 Item2로 형변환.

하지만 적절하지않은 타입으로의 형변환은 런타임 Exception 을 발생시킵니다. 예외처리를 위해서 아래와같은 코드들이 추가될 것 입니다.


Object item = items.get(1);
if(item instanceof Item1) {
	...
} else if (item instanceof Item2) {
	...
}

instanceof를 사용하는 것은 좋지않은 방식임이 분명합니다.

Probably most of you have already heard that using “instanceof” is a code smell and it is considered as a bad practice.

Java 5부터 추가된 Generic이라는 것이 있습니다.

Generic

형변환에 의해 발생할 수 있는 문제점을 컴파일 시점에 점검할 수 있는 방법입니다.

사용방법은 간단합니다.

package d.generic;
import java.io.Serializable;

public class CastingGenericDTO<T> implements Serializable {
	private T object;
    public void setObject(T obj) {
    	this.object = obj;
    }
    public T getObject() {
    	return this.object;
    }
}

<T>꺽쇠로 매개변수화 타입(parameterized type)를 정의해서 사용하면됩니다.

매개변수화 타입의 이름에도 컨벤션이 있으니 지키는 것이 좋습니다.

  • E: 요소 (Element, 자바 컬렉션Collection에서 주로 사용됨)
  • K: 키(key) e.g HashMap의 Key Type
  • T: 타입(type)
  • V: 값(value) e.g HashMap의 Value Type
  • S, U, V: 두 번째, 세 번째, 네 번째에 선언된 타입

아마 이렇게 제네릭을 활용해서 작성하면 형 변환을 하는 로직이 사라지게됩니다. (instanceof 키워드를 사용할 일이 거의 없어지기 때문에 가독성이 좋아질 것입니다.) 왜냐하면 코드에서 명시적으로 타입을 지정하기 때문에 잘못된 타입으로 치환하면 컴파일 자체가 안되기 때문입니다.

Wildcard in Generic

Generic 타입을 사용할 때 여러가지의 타입을 사용하는 제네릭타입을 인자로 받고 싶을 때를 위해 Wildcard라는 것을 사용합니다.

public void wildcardStringMethod(WildcardGeneric<?> c) {
	...
}

위 코드에서는 어떤 타입이 적용된 WildcardGeneric 객체라도 인자로 받을 수 있는 함수가 됩니다.

bounded wildcard

public void wildcardStringMethod(WildcardGeneric<? extends Car> c) {
	...
}

필요하다면 범위가 제한된 wildcard를 사용가능합니다. 이또한 컴파일 시간에 정확한 타입이 사용됐는지 검증될 수 있습니다.

Generic Method

Class 선언과 무관하게 특정 Method에서 사용될 매개변수의 Type을 Generic하게 적용하고 싶을 수 도 있습니다.


public class GenericWildcard {
	public statuc <T> void genericMethod(T value) { ... }
}

위와 같이 사용하면 Generic하게 Mehtod를 사용할 수 있습니다. Generic Class를 선언할 때와는 달리 따로 <>꺽쇠를 사용해서 타입을 명시하지않고 매개변수를 넣어서 암묵적으로 타입 처리를 할 수 있다.

GenericWildcard.genericMethod("Hello"); // 암묵적으로 T는 String Type으로 치환된다.

정리

아주 간단히 사용법 정도만 남겼습니다. 생각보다 제네릭을 활용할 경우가 많지않을 수 있지만 잘 사용한다면 견고한 설계를 가능하게 해줍니다.

기본적인 내용만 정리하는 글이었고 Effective Java에서 설명하는 Generic 관련 주제들로 넘어가겠습니다.

  • Raw Type은 사용하지말라!
  • 비검사 경고를 제거하라!
  • 배열보다는 리스트를 사용하라!
  • 이왕이면 제네릭 타입으로 만들라!
  • 이왕이면 제네릭 메서드로 만들라!
  • 한정적 와일드 카드를 사용해 API요연성을 높이라!
  • 제네릭과 가변인수를 함께 쓸 때는 신중해라
  • 타입 안전 이중 컨테이너를 고려하라!
profile
Scratch, Under the hood, Initial version analysis

0개의 댓글