본문 바로가기

Backend/Java Spring

[Java] 제너릭(Generic)

public class ClassName <E extends Comparable<? super E>> { ... }

 

자바 코드를 보다가 위와 같은 표현이 나오면 도망가기에 급급하였다. '뭐 대충 제너릭 써서 표현하였네' 이정도였지. 해당 코드가 의미하는 바를 생각해볼 여유는 없었다. 오늘 한번 제너릭에 대해 정리해보려고 한다. stranger's lab 님의 포스트를 상당 부분 참고하였다.

 

제너릭 (Generic) 

'일반적인' 이라는 뜻이다. 데이터 타입의 일반화를 의미한다.

ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<T> list2 = new ArrayList<T>();

 

list1 의 경우는 Integer 타입이다. 만일 list1 에 다른 타입의 데이터(String, Character)를 집어 넣는다면 에러가 날 것이다. 

list2는 제너릭 타입이다. 사용하기에 따라 String 데이터를 넣을수도 있고, Interger 데이터를 넣을 수 있다. 특정 타입을 미리 지정하는 것이 아닌 외부에서 지정하는 것을 의미한다. 

 

제너릭의 장점

  • 컴파일 단계에서 타입 안정성 확보
  • 다양한 타입에 의해 재사용

제너릭 클래스, 인터페이스 

public class ClassName <T> { ... }
public interface InterfaceName <T> { ... }

 

기본적으로 위와 같이 쓰인다. T는 타입 파라미터 (type paramter) 라 불리며 해당 코드블럭 내에서만 유효하다. 타입 파라미터로는 참조 타입(Reference type) 밖에 올 수 없다. 

 

제너릭 메소드

public <T> T genericMethod(T o) {}

 

기본적으로 위와 같이 쓰인다. 타입 파라미터는 메소드의 코드 블럭 내에서만 유효하다. 클래스의 타입 파라미터와 독립적으로 사용할 수 있다. 이러한 특성은 정적 메서드에서 두드러진다. 정적 메소드는 클래스에서 객체가 생성되기 전에 이미 메모리에 올라가기 때문이다. 

 

제한된 제너릭 (Bounded generics) 

위에서 본 예시들의 타입 파라미터에는 제한이 없었다. String 이든, Number든 어떤 타입이든 올 수 있었다. 하지만 특정 경우에는 제한된 타입만 받기를 원할 것이다. 제한된 제너릭 (Bounded generics)은 이 지점에서 시작되었다. 

 

<K extends T>	// T와 T의 자손 타입만 가능 (K는 들어오는 타입으로 지정 됨)
<K super T>	// T와 T의 부모(조상) 타입만 가능 (K는 들어오는 타입으로 지정 됨)
 
<? extends T>	// T와 T의 자손 타입만 가능
<? super T>	// T와 T의 부모(조상) 타입만 가능
<?>		// 모든 타입 가능. <? extends Object>랑 같은 의미

 

만일 class <K extends Number> 라고 한다면 타입 파라미터로 올 수 있는 타입은 Number 와 Number 자식 타입 (Long, Integer, Byte .. ) 이다. 

 

와일드 카드 (Wildcard)

? 이다. 일반적으로 모든 타입을 나타낸다. 예를 들어, List <?> 는 모든 종류의 리스트를 나타낸다. 

아래는 T extends Number 와 ? extends Number를 비교하였다. 

<T extends Number>  <? extends Number>
Number class 와 그의 하위 타입을 다룬다. 
T를 참조할 수 있다.  ?를 참조할 수 없다. 
list.add((T) new Integer(40)); -> 정상 list.add((?) new Integer(40)); -> 에러 

 

일반적 예시 

public class ClassName <E extends Comparable<? super E>> { ... }

 

 

이제는 위의 코드가 의미하는 바를 읽을 수 있다. 위의 코드는 일반적으로 TreeMap, TreeSet 과 같이 값을 정렬하는 클래스에서 자주 보인다. 아래의 단계를 거치면 어떻게 저런 형태가 나왔는지 이해할 수 있다. 

 

1. E extends Comparable<E> 

 

Comparable<E> 나 그 하위 클래스인 E 를 나타낸다. E 객체는 반드시 Comparable 을 구현해야 한다. 

코드로 보면 아래와 같을 것이다. 

public class SaltClass <E extends Comparable<E>> { ... }
 
public class Student implements Comparable<Student> {
	@Override
	public int compareTo(Person o) { ... };
}
 
public class Main {
	public static void main(String[] args) {
		SaltClass<Student> a = new SaltClass<Student>();
	}
}

 

 

2. <? super E> 

 

실제 코드는 E extends Comparable<E> 가 아닌 E extends Comparable <? super E> 이다. <? super E> 는  E와 E의 부모 타입을 뜻한다. 부모 타입에서 Comparable 을 구현한 경우를 의미한다. 

public class SaltClass <E extends Comparable<E>> { ... }	// Error가능성 있음
public class SaltClass <E extends Comparable<? super E>> { ... }	// 안전성이 높음
 
public class Person {...}
 
public class Student extends Person implements Comparable<Person> {
	@Override
	public int compareTo(Person o) { ... };
}
 
public class Main {
	public static void main(String[] args) {
		SaltClass<Student> a = new SaltClass<Student>();
	}
}

 

 

위의 코드를 보면 이해가 편하다.

E extends Comparable <? super E> 은 한마디로 자기 자신 및 부모 타입과 비교할 수 있는 타입을 뜻한다. 

 

 

[참고한 글]

https://st-lab.tistory.com/153

 

자바 [JAVA] - 제네릭(Generic)의 이해

정적언어(C, C++, C#, Java)을 다뤄보신 분이라면 제네릭(Generic)에 대해 잘 알지는 못하더라도 한 번쯤은 들어봤을 것이다. 특히 자료구조 같이 구조체를 직접 만들어 사용할 때 많이 쓰이기도 하고

st-lab.tistory.com

https://stackoverflow.com/questions/18187005/java-generic-type-difference-between-list-extends-number-and-list-t-exten

 

Java Generic type : difference between List <? extends Number> and List <T extends Number>

Java Generic type : what is the difference between (1) List <? extends Number> (2) List <T extends Number> as per my understanding (1) List <? extends Number> is the Read...

stackoverflow.com

 

'Backend > Java Spring' 카테고리의 다른 글

[Java] 직렬화와 역직렬화  (0) 2023.11.15
[Java] 공유 중인 가변 데이터  (0) 2023.11.12
[Spring] @Transactional  (2) 2023.11.09
[Spring] singleton scope  (0) 2023.11.05