자바 제네릭 마스터하기: 정렬 단순화와 타입 안전성 강화
단일 정렬 메서드를 작성하여 Integer 배열, String 배열, 또는 순서를 지원하는 어떤 타입의 배열이든 정렬할 수 있다면 좋을 것입니다.
자바 제네릭(Generic) 메서드와 제네릭 클래스는 프로그래머가 단일 메서드 선언으로 관련 메서드 집합을, 단일 클래스 선언으로 관련 타입 집합을 각각 지정할 수 있게 합니다.
제네릭은 컴파일 타임 타입 안전성을 제공하여 프로그래머가 잘못된 타입을 컴파일 시점에 잡을 수 있게 합니다.
자바 제네릭 개념을 사용하면 객체 배열을 정렬하는 제네릭 메서드를 작성한 뒤, Integer 배열, Double 배열, String 배열 등으로 제네릭 메서드를 호출하여 배열 요소들을 정렬할 수 있습니다.
제네릭 메서드(Generic Methods)
단일 제네릭 메서드 선언으로 서로 다른 타입의 인수들과 함께 호출될 수 있습니다. 제네릭 메서드에 전달된 인수들의 타입에 기반하여 컴파일러가 각 메서드 호출을 적절히 처리합니다. 제네릭 메서드 정의 규칙:
- 모든 제네릭 메서드 선언은 메서드 반환 타입 앞에 오는 꺾쇠 괄호(
<와 >)로 구분된 타입 매개변수 섹션을 가짐 (<E>가 다음 예제에서 해당)
- 각 타입 매개변수 섹션은 콤마로 구분된 하나 이상의 타입 매개변수들을 포함. 타입 매개변수는 타입 변수라고도 하며 제네릭 타입 이름을 지정하는 식별자
- 타입 매개변수들은 반환 타입을 선언하고 제네릭 메서드에 전달된 인수들의 타입인 실제 타입 인수들의 플레이스홀더로 사용될 수 있음
- 제네릭 메서드 본문은 다른 메서드처럼 선언. 타입 매개변수들은 참조 타입만 표현할 수 있으며 기본 타입(int, double, char 등)은 표현할 수 없음
예제: 단일 제네릭 메서드로 다양한 타입 배열 출력
public class GenericMethodTest {
// 제네릭 메서드 printArray
public static void printArray(E[] inputArray) {
// 배열 요소들 출력
for (E element : inputArray) {
System.out.printf("%s ", element);
}
System.out.println();
}
public static void main(String args[]) {
// Integer, Double, Character 배열 생성
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println("Array integerArray contains:");
printArray(intArray); // Integer 배열 전달
System.out.println("\nArray doubleArray contains:");
printArray(doubleArray); // Double 배열 전달
System.out.println("\nArray characterArray contains:");
printArray(charArray); // Character 배열 전달
}
}
실행 결과
Array integerArray contains: 1 2 3 4 5
Array doubleArray contains: 1.1 2.2 3.3 4.4
Array characterArray contains: H E L L O
제한된 타입 매개변수(Bounded Type Parameters)
타입 매개변수에 허용되는 타입의 종류를 제한하고 싶을 때가 있습니다. 예를 들어 숫자를 다루는 메서드는 Number나 그 하위 클래스 인스턴스만 받고 싶을 수 있습니다. 이것이 제한된 타입 매개변수의 용도입니다.
제한된 타입 매개변수를 선언하려면 타입 매개변수 이름 뒤에 extends 키워드와 상한(upper bound)을 나열합니다.
예제: 세 Comparable 객체 중 최댓값 반환하는 제네릭 메서드
다음 예제는 클래스에서는 "extends", 인터페이스에서는 "implements"를 의미하는 일반적인 의미로 extends가 어떻게 사용되는지 보여줍니다.
public class MaximumTest {
// 세 Comparable 객체 중 최댓값 결정
public static > T maximum(T x, T y, T z) {
T max = x; // 초기에 x가 최댓값이라 가정
if (y.compareTo(max) > 0) {
max = y; // y가 현재까지 최댓값
}
if (z.compareTo(max) > 0) {
max = z; // z가 이제 최댓값
}
return max; // 최댓값 객체 반환
}
public static void main(String args[]) {
System.out.printf("Max of %d, %d and %d is %d\n\n", 3, 4, 5, maximum(3, 4, 5));
System.out.printf("Max of %.1f, %.1f and %.1f is %.1f\n\n", 6.6, 8.8, 7.7, maximum(6.6, 8.8, 7.7));
System.out.printf("Max of %s, %s and %s is %s\n", "pear", "apple", "orange", maximum("pear", "apple", "orange"));
}
}
실행 결과
Max of 3, 4 and 5 is 5
Max of 6.6, 8.8 and 7.7 is 8.8
Max of pear, apple and orange is pear
제네릭 클래스(Generic Classes)
제네릭 클래스 선언은 클래스 이름 뒤에 타입 매개변수 섹션이 온다는 점을 제외하면 비제네릭 클래스 선언과 같습니다.
제네릭 메서드와 마찬가지로 제네릭 클래스의 타입 매개변수 섹션은 콤마로 구분된 하나 이상의 타입 매개변수들을 가질 수 있습니다. 이러한 클래스들을 매개변수화된 클래스(parameterized classes) 또는 매개변수화된 타입(parameterized types)이라고 합니다. 왜냐하면 하나 이상의 매개변수를 받아들이기 때문입니다.
예제: 제네릭 클래스 정의
public class Box {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box integerBox = new Box();
Box stringBox = new Box();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
System.out.printf("Integer Value: %d\n\n", integerBox.get());
System.out.printf("String Value: %s\n", stringBox.get());
}
}
실행 결과
Integer Value: 10
String Value: Hello World