시작하기에 앞서서 어김없이 내가 알고 있는 제네릭을 적어본다.
1. List
2. Map
3. Set
주로 많이 썼던 제네릭이라고는 List 랑 Map 이였던 것 같다.
클레스라는 개념을 잘 모를 때는, Map<String, List<Map<Integer,Double>>> 이런식으로 복잡하게도 사용했었다...
클레스에 대해서 조금 아니까 Map<String, User> 이런식으로 조금 더 가독성이 좋게 만들었다.
그때 당시에는 내가 하는 행동들이 객체지향을 향해서 가는 줄도 모르고 그냥 재밌어서 했다.
아무것도 모르고 사용했어도,
이게 다 좋은 경험으로 남아있다. ( 놀이는 최고의 연구! )
위는 그냥 제네릭에 대해서 어떤인식을 가졌고 어떻게 사용했었는지 ?
본론으로 돌아와서 제네릭에 대해서 조금 더 자세하게 알아보도록 하겠다.
제네릭이라는 용어는 영어 단어 generic에서 유래했으며, 이는 일반적인, 보편적인, 혹은 특정되지 않은의 의미를 가집니다.
즉, 특정 데이터 타입에 국한되지 않고, 다양한 타입에 대해 범용적으로 사용할 수 있는 코드를 작성할 수 있게 해 주기 때문에 이러한 의미인 것 같다.
List<String> , List<Integer>, List<Class ?> 확실히 데이터 타입에 국한되지 않고 모든 타입을 받을 수 있다는 게 보인다.
JAVA - Generic (소개)
공식 소개 문서에 따르면,
List myIntList = new LinkedList(); // 1
myIntList.add(new Integer(0)); // 2
Integer x = (Integer) myIntList.iterator().next(); // 3
3번 줄에 있는건 런타임 오류가 발생할 수 있고, 형변환도 필수적으로 진행해야한다.
만든이들은 이 부분을 해결하려 만든게 제네릭이다.
List<Integer>
myIntList = new LinkedList<Integer>(); // 1'
myIntList.add(new Integer(0)); // 2'
Integer x = myIntList.iterator().next(); // 3'
기존은 List 만 있지만, 바뀐점은 List<Integer> 이 생겼다는 점이다. 이것이 바로 제네릭이다.
3번줄의 형변환히 사라졌다.
사소해보일 지 몰라도, cast 방식은 그때마다 개발자가 형 변환을 진행해줘야 하지만, 제네릭은 만들 때 이미 선언을 하고 사용하기 때문에 항상 Integer 이 사실이다. 를 증명하는 것과 같다.
이는 가독성과, 견고성을 늘린 것 이다.

일단 이런식으로 패키지를 짜봤으며, 처음에 시도할때 test.first 패키지는 없다 생각하고
Pair , Box Class 만 봐주세요.
package generic.single;
public class Box<T> {
private T value;
public Box(T value) {
this.value = value;
}
public void set(T v){
this.value = v;
}
public T getValue() {
return value;
}
} - 제네릭 ( 1개의 타입 매개변수만 받을 수 있게 설정 )
package generic.multi;
public class Pair<K, V> {
private final K key;
private final V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
} - 복수 제네릭 ( 2개의 타입 매개변수만 받을 수 있게 설정 )

여기서는 주석으로 처리했지만, 전부 정상적으로 실행되는 모습을 볼 수 있다.
근데 눈치가 빠른 사람들은 뭔가 이상한 점을 발견했을 것 이다.
나는 위에서
List myIntList = new LinkedList();
List<Integer> myIntList = new LinkedList<Integer>();
내가 보여준 거랑 다르다? new Box<>("TEST") <---> LinkedList<Integer>()
하지만, 이상하게 생각하지 않아도 된다.
이는 Java 7이상부터 다이아몬드 연산자를 사용해서 간단하게 표현한 것이다.
위의 공식문서를 참고하면
Type Inference and Instantiation of Generic Classes
You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (<>) as long as the compiler can infer the type arguments from the context. This pair of angle brackets is informally called the diamond.
For example, consider the following variable declaration:
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
You can substitute the parameterized type of the constructor with an empty set of type parameters (<>):
Map<String, List<String>> myMap = new HashMap<>();
Note that to take advantage of type inference during generic class instantiation, you must use the diamond. In the following example, the compiler generates an unchecked conversion warning because the HashMap() constructor refers to the HashMap raw type, not the Map<String, List<String>> type:
Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning
<> 이걸 다이아몬드라고 부른다고... 즉 쉽게 말하면 자바 7 이상부터는 뒤에 붙는 타입 매개변수는 생략해도 된다는 소리다.
물론 new ArrayList<>(); 이런식으로 호출할 경우는 new ArrayList<String>(); 로 타입 매개변수를 지정해줘야한다.
한번씩 다른 사람의 깃헙을 보다보면 아래와 같이 <T> 같이 타입 매개변수가 붙어있는 걸 볼 수 있다.
이걸 메서드 제네릭이라고 한다.
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
메서드 <T> 타입의 배열을 가져와서 출력한다.
쉽게 말하면 어떤 타입의 배열이든 받을 수 있다는 뜻 이다.
제네릭 공식 문서를 보면 와일드카드 도 있는데 오늘은 이해하는 만큼만 적고, 와일드카드는 다음에 적도록 하겠습니다.
새로 알게된 점
1. 제네릭은 내가 List Map 등 뿐만이 아니라 내가 직접 만들어서 사용할 수 있다.
2. 이걸 다형성과 추상화 개념을 넣어서 유지보수와 가독성, 유연함을 줄 수 있다.
3. 이건 굉장히 많이 사용할 것 같다.