오늘은 이어서 Java의 Generic 3번째
에 대해서 알아보자.
실제로 나는 이 개념을 이해하기 위해 유튜브, 블로그 등등 매우 많은 자료를 찾아봤다.
https://youtu.be/K1iu1kXkVoA <- 실제로 많은 도움을 준 유튜버
공식문서
정말 많은 내용들이 있지만, 일단 와일드 카드는 타입을 유연하게 받으면서도 타입 안정성을 지키고 싶을 때 사용한다.
이게 무슨 말이냐?
public void printAll(List<? extends Animal> list) {
for (Animal a : list) {
System.out.println(a);
}
}
여기서 보이는 <?> <- 이게 와일드 카드다.
이와 같은 코드가 있을 경우 해당 메서드는 Animal 을 구현받은 클레스만 받을 수 있다는 뜻이다.
만약 Animal 을 구현받지 않는 메서드가 오면 오류를 일으킨다.
예제)
public void printList(List<?> list) {
for (Object element : list) {
System.out.println(element);
}
}
어떤 타입이든 괜찮다.
대신, 무제한 와일드카드 는 읽기 전용 이다.
그리고 위 예제에서 list.add(...) 한다고 하면 안된다.
이유는 타입이 뭔지 몰라서 위험하니까
그리고 중요한건 무제한 와일드카드는 무조건 받는 값을 오브젝트로 출력한다.
여기에 값을 넣는다는건..? 흠.. 제네릭은 무조건 타입이 일치해야 한다는 것 ( 2 ) 편에서 배웠다.
모르겠으면 다시 공부하자.
public void printNumbers(List<? extends Animal> list) {
for (Animal n : list) {
System.out.println(n);
}
}
Animal 을 구현받은 클레스만 받을 수 있다.
내가 만약 Cat.class 와 Dog.class 를 구현시켜줬다면 해당 메서드를 호출할 수 있다.
그리고 이것도 읽기전용 이다.
?? 왜냐면 컴파일러는 이 리스트가 실제로 List<Dog>인지 List<Cat>인지 모르기 때문이다.
그런데 여기에 Mouse 넣으면? → 타입이 뭔지 모르니까 아예 추가를 막아버리는 것이다.
상한 와일드카드 까지 배웠다면 조금씩 헷갈리기 시작할 때 더 헷갈리는 개념이 등장한다.
List<Object> objs = new ArrayList<>();
List<Animal> animals = new ArrayList<>();
addAnimals(objs); // OK
addAnimals(animals); // OK
public void addAnimals(List<? super Cat> list) {
list.add(new Cat("떼걸룩"));
list.add(new Cat("하하"));
}
갑자기 헷갈리는 하한 와일드카드 가 생겨버린다.
하지만 어려워할 필요는 없다.
해당 기능은 쓰기 전용 카드 이므로 타입을 정확히 해야한다.
대신 읽기는 Object 로 체크되므로 읽기에는 비추천 이다.
와일드카드를 쓰기 위해선 PECS 공식이 필수다.
P roducer - 값을 꺼내서 사용하는 쪽 ( 꺼내서 읽는 거임 )
E xtends - 하위 타입을 허용한다.
C onsumer - 값을 리스트에 추가하는 쪽 ( 소비하는 쪽임 )
S uper - 상위 타입을 허용한다.
즉 PE 는 extends 읽기
CS 는 super 쓰기이다.
이것만 알아도 구분된다.