JUST WRITE

Generic 본문

Programing/Java

Generic

천재보단범재 2021. 10. 15. 11:42
이 글은 Baeldung 사이트 'The Basics of Java Generics'를 해석, 정리한 글입니다.

 

Generic

Generic

Generic은 데이터의 타입을 일반화하는 것을 의미한다.

Class 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법이다.

JDK 5.0부터 Bug를 줄이고자 도입되었다.

Generic 필요성

예를 들어 Integer 타입을 저장하는 List를 만들려고 한다.

List list = new LinkedList();
list.add(new Integer(1));
list.add(new Integer(2));
list.add(new Integer(3));
// 타입이 불분명한 상황
Integer i = list.iterator().next();
// 명시적으로 타입 변환
Integer i = (Integer) list.iterator().next();
// Runtime 에러!!!
String str = (String) list.iterator().next();

위 코드에서 List는 어느 타입의 Object든 담을 수 있다.

개발자가 맨 마지막 라인처럼 타입 변환 실수를 하게 되면 Runtime 에러가 발생할 수 있다.

이러한 상황을 방지하기 위해 compile 단계에서 어떤 타입인지 확정할 때 쓰는 것이 Generic이다.

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

<>를 추가함으로써 compile 단계에서 타입을 확정 지을 수 있다.

Generic Method

Generic을 이용해서 1개의 Method를 정의하고 다양한 타입 Argument를 부를 수 있다.

Method 정의

// Generic 1개 사용
public <T> List<T> fromArrayToList(T[] a) {   
    return Arrays.stream(a).collect(Collectors.toList());
}

// Generic 2개 이상 사용
public static <T, G> List<G> fromArrayToList(T[] a, Function<T, G> mapperFunction) {
    return Arrays.stream(a)
      .map(mapperFunction)
      .collect(Collectors.toList());
}

// Generic Method 사용
@Test
public void givenArrayOfIntegers_thanListOfStringReturnedOK() {
    Integer[] intArray = {1, 2, 3, 4, 5};
    List<String> stringList
      = Generics.fromArrayToList(intArray, Object::toString);
 
    assertThat(stringList, hasItems("1", "2", "3", "4", "5"));
}

위 코드처럼 Generic을 사용하는 Method를 사용할 수 있다.

선언부쪽에 명시적으로 <T>, <T, G>처럼 Generic을 사용하는 것을 알려준다.(Return 없을 경우(void)에도)

Oracle에서는 위에 T, G 처럼 Generic Type 문자를 대문자로 지정할 것을 추천한다.

Java Collection에서 Generic Type 문자를 T는 Type, K는 Key, V는 Value로 의미한다.

Generic Type 제한

Generic을 사용할 경우 모든 Type을 다루는 게 아닌 일부 Type만 처리하도록 제한할 수 있다.

하위 Class나 상위 Class 내로 제한이 가능하다.

public <T extends Number> List<T> fromArrayToList(T[] a) {
    ...
}

위에서 extends라는 키워드 통해 해당 T에는 Number Class의 하위 Class만 들어오도록 제한하였다.

Generic Type 제한할때 Interface, Abstract Class 모두 extends라는 키워드 사용한다.

<T extends Number & Comparable>

2개 이상의 조건으로 제한이 가능하다.

정의한 순서대로 Type을 체크하게 된다.

Wildcard

Wildcard는 Java에서 unknown type(?)로 사용된다.

Wildcard에는 3가지 종류가 있다.

  • Unbounded Wildcard -> 제한을 두지 않고 모든 Type 가능
  • Upper Bounded Wildcard -> extends 키워드를 이용해 특정 Class의 하위 Class로만 제한
  • Lower Bounded Wildcard -> super 키워드를 이용해 특정 Class의 상위 Class로만 제한
// Unbound Wildcard
public static void paintAllBuildings(List<?> buildings) {
    ...
}
// Upper Bounded Wildcard
public static void paintAllBuildings(List<? extends Building> buildings) {
    ...
}
// Lower Bounded Wildcard
public static void paintAllBuildings(List<? super House> buildings) {
    ...
}

Type 제거

Generic을 Runtime시 Overhead를 유발하지 않도록 Compile Time에 Type이 제거된다.

예를 들어 Unbounded Type은 Object로 교체된다.

// Example 1
// compile 전
public List<Object> withErasure(List<Object> list) {
    return list.stream().collect(Collectors.toList());
}

// compile 후
public List withErasure(List list) {
    return list.stream().collect(Collectors.toList());
}

// Example 2
// compile 전
public <T extends Building> void genericMethod(T t) {
    ...
}

// compile 후
public void genericMethod(Building t) {
    ...
}

Generic과 PrimitiveType

Generic Type으로 Primitie Type을 사용할 수 없다.

Generic이 Compile Time 특성이고 Type 제거라는 특성을 통해 Object로 변할 수 있다.

하지만 Primitive Type은 Object를 상속받은 게 아니기 때문에 불가능하다.

대신 Wrapper Class를 활용하여 해결이 가능하다.

// 불가능!!!
List<int> list = new ArrayList<>();
list.add(17);
// Wrapper Class로 대체, 가능!!!
List<Integer> list = new ArrayList<>();
list.add(17);

[참고사이트]

728x90
반응형

'Programing > Java' 카테고리의 다른 글

Log4j vs Logback  (0) 2021.10.26
Interface vs Abstract  (0) 2021.10.21
LinkedList  (0) 2021.10.11
ArrayList  (0) 2021.10.10
Java SE vs Java EE  (0) 2021.10.03
Comments