프로그래밍 공부
[자바] #14. Collections 클래스 안의 여러 알고리즘 구현 메서드들 본문
Collections 클래스 안에는, 다양한 알고리즘을 구현한 메서드들이 있다.
📢 정렬 (Collections.sort)
public static <T> void sort(List<T> list, Comparator<? super T> c)
여기서 ? super T는 제네릭 타입의 와일드카드로, T 타입과 T의 상위 타입(Superclass) 를 모두 허용한다.
즉, T 또는 T의 부모 타입에 대해 비교를 수행할 수 있는 Comparator를 사용할 수 있도록 한다.
💡 왜 <? super T>를 사용할까?
- 유연성(Flexibility): 상위 타입의 비교자를 재사용할 수 있다.
- 안전성(Type Safety): 하위 클래스의 객체가 상위 클래스의 필드를 비교할 때도 타입 안정성을 보장한다.
🧩 예제: ? super T의 필요성 이해
✅ 클래스 구조
class Animal implements Comparable<Animal>{
String name;
public Animal(String name) {
this.name = name;
}
@Override
public int compareTo(Animal other) {
return this.name.compareTo(other.name);
}
}
class Dog extends Animal {
int age;
public Dog(String name, int age) {
super(name);
this.age = age;
}
}
🚀 예제 코드: Comparator<? super T> 활용
import java.util.List;
import java.util.Collections;
public class ComparatorSuperExample {
public static void main(String[] args) {
List<Dog> dogs = Arrays.asList(
new Dog("Buddy", 5),
new Dog("Charlie", 3),
new Dog("Max", 7)
);
// ✅ Animal 타입 기준 Comparator (name 기준 비교)
Comparator<Animal> animalComparator = (a1, a2) -> a1.name.compareTo(a2.name);
// ✅ Collections.sort에서 Comparator<? super T> 덕분에 사용 가능
Collections.sort(dogs, animalComparator);
for (Dog dog : dogs) {
System.out.println(dog.name + " (" + dog.age + "살)");
}
}
}
여기서 T는 dog.
Comparator<? super T>가 아니었다면, Comparator<Animal>은 List<Dog>에 바로 사용할 수 없다.
? super T 덕분에 Dog의 상위 타입인 Animal의 비교자도 사용할 수 있는 것.
💎 핵심 요약
- ? super T: T와 T의 상위 클래스 모두 허용하여 더 일반적인 비교자를 사용할 수 있도록 함.
- 실제 사용 사례: Collections.sort()에서 부모 타입의 Comparator를 재사용할 때 유용.
- "PECS" 원칙:
- Producer Extends, Consumer Super
- 데이터를 소비하는 경우(Comparator)에는 super 사용.
📢 찾기 (Collections.binarySearch)
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
💡 매개변수 설명
List<? extends Comparable<? super T>> list
- 리스트의 요소 타입이 T 또는 T의 하위 타입(? extends T)이어야 한다.
- 각 요소는 Comparable<? super T>를 구현해야 한다. 즉, 리스트의 요소는 T와 T의 상위 타입과 비교할 수 있어야.
🎯 List<? extends Comparable<? super T>>의 의미
| 부분 | 의미 |
| ? extends | 리스트 요소가 T 또는 T의 하위 클래스일 수 있도록 허용 (읽기 전용으로 안전) |
| Comparable<? super T> | 리스트 요소가 T 및 T의 상위 클래스와 비교할 수 있어야 함 (타입 유연성 증가) |
import java.util.List;
import java.util.Collections;
public class SuperTypeComparableExample {
public static void main(String[] args) {
List<Dog> dogs = Arrays.asList(
new Dog("Buddy"),
new Dog("Charlie"),
new Dog("Max")
);
// 정렬 (Animal의 compareTo 사용)
Collections.sort(dogs);
// Dog 객체를 찾음 (Animal의 비교 규칙을 따름)
Dog target = new Dog("Charlie");
int index = Collections.binarySearch(dogs, target);
System.out.println("Charlie의 인덱스: " + index);
}
}
🌟 왜 <? extends Comparable<? super T>>를 사용했을까?
여기서:
- T는 Dog.
- Dog는 Animal의 하위 타입.
- 리스트(List<Dog>)의 요소는 Animal 타입과 비교할 수 있어야.
- Animal이 Comparable<Animal>을 구현했기 때문에 Dog도 자동으로 Animal과 비교할 수 있다.
- List<? extends Comparable<? super T>> 덕분에 Dog 리스트에서도 Animal 비교 방식을 사용할 수 있다.
👉 유연성과 재사용성을 보장하는 설계!
🧩 만약 Comparable<T>만 사용했다면?
public static <T> int binarySearch(List<? extends Comparable<T>> list, T key)
이 경우 Comparable<Dog>가 필요하다.
하지만 Dog는 Comparable<Dog>를 구현하지 않았고, Animal만 Comparable<Animal>을 구현했기 때문에 컴파일 오류가 발생했을 것이다.
정리하자면:
| 구문 | 의미 | 예시 |
| Comparable<T> | 정확히 같은 타입(T)끼리만 비교 허용 | Comparable<Animal>은 Animal만 비교 |
| Comparable<? super T> | T와 T의 상위 타입과도 비교 허용 (더 유연함) |
Comparable<? super Dog>는 Dog와 Animal 모두 비교 가능 |
📢 복사 (Collections.copy)
public static <T> void copy(List<? super T> dest, List<? extends T> src)
💡 매개변수 설명
1️⃣ List<? super T> dest (복사 대상 리스트)
- 의미:
- dest 리스트는 T 타입 또는 T의 상위 타입(Superclass) 을 포함할 수 있다.
- 왜 <? super T>인가?
- dest는 src의 요소를 받아들여서 소비(=저장)(consumer) 하는 역할을 한다.
- "PECS 원칙":
- Producer Extends
- Consumer Super
- 즉, dest가 T의 상위 타입을 허용해야 T 타입 요소를 안전하게 저장할 수 있다.
List<Object> dest = new ArrayList<>(Arrays.asList(new Object[3]));
List<String> src = Arrays.asList("A", "B", "C");
Collections.copy(dest, src); // ✅ 가능 (Object는 String의 상위 타입)
2️⃣ List<? extends T> src (복사할 리스트)
- 의미:
- src 리스트는 T 타입 또는 T의 하위 타입(Subclass) 을 포함할 수 있다.
- 왜 <? extends T>인가?
- src는 dest로 데이터를 제공(produce) 한다.
- 따라서 T의 하위 타입을 포함할 수 있도록 허용하여 유연성을 증가시킨다.
List<Number> dest = new ArrayList<>(Arrays.asList(0, 0, 0));
List<Integer> src = Arrays.asList(1, 2, 3);
Collections.copy(dest, src); // ✅ 가능 (Integer는 Number의 하위 타입)
⚡ 왜 이렇게 설계되었을까? (PECS 원칙)
| 역할 | 와일드카드 | 예시 |
| 제공자 (Producer) | ? extends T | List<? extends Number> → Integer, Double 제공 가능 |
| 소비자 (Consumer) | ? super T | List<? super Integer> → Number, Object 허용 |
✍ 실제 코드 예제
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
public class CopyExample {
public static void main(String[] args) {
List<Object> dest = new ArrayList<>(Arrays.asList(new Object[3]));
List<String> src = Arrays.asList("Apple", "Banana", "Cherry");
Collections.copy(dest, src);
System.out.println(dest); // 출력: [Apple, Banana, Cherry]
}
}
❓ 그럼 실제 코드 예제에서 T가 String이야? T가 String인 줄은 어떻게 알아, 컴파일러가?
↓ ↓ ↓ ↓ ↓
🎯 컴파일러가 T를 추론하는 방식
copy 메서드에서, T는 메서드 호출 시 컴파일러가 자동 추론하는 제네릭 타입이다.
🚀 타입 추론 과정
- src의 타입 분석:
- src는 List<String>.
- src가 List<? extends T>이어야 하므로, T는 String이거나 String의 상위 타입이어야 한다.
- dest의 타입 분석:
- dest는 List<Object>.
- dest가 List<? super T>이어야 하므로, T는 Object이거나 Object의 하위 타입이어야 한다.
- 조건 통합:
- src: T ≤ String (즉, T가 String의 상위 타입이어야 함)
- dest: T ≥ Object (즉, T가 Object의 하위 타입이어야 함)
⚡ 결론:
- T가 동시에 String의 상위 타입이면서 Object의 하위 타입이어야 하므로, 가장 구체적인 타입은 "String"이다.
- 따라서 컴파일러는 T를 String으로 추론한다.
✅ 예제 변경 : T가 Integer인 경우
List<Number> dest = new ArrayList<>(Arrays.asList(0, 0, 0));
List<Integer> src = Arrays.asList(1, 2, 3);
Collections.copy(dest, src); // ✅ 성공
분석:
- src: List<Integer> ⇒ T ≥ Integer
- dest: List<Number> ⇒ T ≤ Number
- 따라서 Integer ≤ T ≤ Number ⇒ T = Integer
💎 핵심 요약
- 컴파일러는 src와 dest 리스트의 타입을 비교하여 가장 구체적인 T를 자동 추론한다.
- ? super T: dest가 T 타입을 받아들일 수 있도록 보장.
- ? extends T: src가 T 타입의 데이터를 제공할 수 있도록 보장.
- "PECS" 원칙을 통해 메서드의 타입 안정성과 유연성을 동시에 확보한다.
'자바' 카테고리의 다른 글
| [자바] #16. 네스티드(Nested) 클래스, 이너(Inner) 클래스 (0) | 2025.02.19 |
|---|---|
| [자바] #15. 열거형, 가변 인자, 어노테이션 (0) | 2025.02.19 |
| [자바] #13. 컬렉션 프레임워크 - 4. Map (0) | 2025.02.16 |
| [자바] #12. 컬렉션 프레임워크 - 3. Queue, Deque (0) | 2025.02.16 |
| [자바] #11. 컬렉션 프레임워크 - 2. Set (HashSet, TreeSet) (0) | 2025.02.16 |