제네릭(Generic)은 여러 형식에 맞게 재사용할 수 있는 코드를 정의하는 기술을 말합니다. 제네릭 코드를 정의할 때는 가상의 형식 이름을 <가상 형식 이름> 처럼 정의하고 사용하는 곳에서 어떠한 형식에 관한 코드를 사용할 것인지 명시하여 사용하는 기법입니다.
먼저 제네릭 클래스를 정의할 때는 클래스명 뒤에 <가상 형식 이름>을 명시하여 만들 수 있습니다.
class 클래스명<가상 형식 이름>{
}
사용하는 곳에서는 가상 형식 이름 대신 구체적으로 사용할 형식을 결정하여 <사용할 형식 이름>을 명시하여 사용합니다.
클래스명<사용할 형식 이름> 변수명 = new 클래스명<사용할 형식 이름>();
다음은 정수 형식을 보관하는 정수 적 배열 클래스와 제네릭 형식으로 원하는 형식을 사용자가 결정할 수 있는 제네릭 동적 배열 클래스를 정의한 것입니다.
▷ 소스 3.1 정수 동적 배열과 제네릭 동적 배열 예
//IntDArray.java //정수 동적 배열 public class IntDArray { int[] buffer; int capacity; int usage; public IntDArray(int capacity){ this.capacity = capacity; buffer = new int[capacity]; usage = 0; } public boolean isEmpty(){ return usage == 0; } public boolean isFull(){ return usage == capacity; } public int size(){ return usage; } public boolean add(int value){ if(isFull()){ return false; } buffer[usage] = value; usage++; return true; } public void viewAll(){ String outstr = String.format("저장소 크기:%d 보관개수:%d",capacity,usage); System.out.println(outstr); for(int i = 0; i<usage;i++){ System.out.print(buffer[i]+" "); } System.out.println(); } }
//DArray.java //제네릭 동적 배열 public class DArray<datatype> { Object[] buffer; int capacity; int usage; public DArray(int capacity){ this.capacity = capacity; buffer = new Object[capacity]; usage = 0; } public boolean isEmpty(){ return usage == 0; } public boolean isFull(){ return usage == capacity; } public int size(){ return usage; } public boolean add(datatype value){ if(isFull()){ return false; } buffer[usage] = value; usage++; return true; } public void viewAll(){ String outstr = String.format("저장소 크기:%d 보관개수:%d",capacity,usage); System.out.println(outstr); for(int i = 0; i<usage;i++){ System.out.print(buffer[i]+" "); } System.out.println(); } }
//Program.java //정수 동적 배열과 제네릭 동적 배열 public class Program { public static void main(String[] args){ System.out.println("==Test IntDArray==="); IntDArray idarr = new IntDArray(10); idarr.viewAll(); idarr.add(3); idarr.viewAll(); idarr.add(2); idarr.viewAll(); idarr.add(6); idarr.viewAll(); System.out.println("==Test DArray<integer>==="); DArray<Integer> darr = new DArray<Integer>(10); darr.viewAll(); darr.add(3); darr.viewAll(); darr.add(2); darr.viewAll(); darr.add(6); darr.viewAll(); System.out.println("==Test DArray<String>==="); DArray<String> darr2 = new DArray<String>(10); darr2.viewAll(); darr2.add("Hello"); darr2.viewAll(); darr2.add("언제나 휴일"); darr2.viewAll(); darr2.add("ehpub.co.kr"); darr2.viewAll(); } }
▷ 소스 3.1 실행 결과
==Test IntDArray=== 저장소 크기:10 보관개수:0 저장소 크기:10 보관개수:1 3 저장소 크기:10 보관개수:2 3 2 저장소 크기:10 보관개수:3 3 2 6 ==Test DArray<integer>=== 저장소 크기:10 보관개수:0 저장소 크기:10 보관개수:1 3 저장소 크기:10 보관개수:2 3 2 저장소 크기:10 보관개수:3 3 2 6 ==Test DArray<String>=== 저장소 크기:10 보관개수:0 저장소 크기:10 보관개수:1 Hello 저장소 크기:10 보관개수:2 Hello 언제나 휴일 저장소 크기:10 보관개수:3 Hello 언제나 휴일 ehpub.co.kr
또한 메소드의 특정 인자 형식을 제네릭 형태로 표현할 수도 있습니다. 이 때 메소드 리턴 형식 이름 앞에 <가상 형식 이름>으로 명시하여 작성합니다. 이러한 메서드를 제네릭 메서드라 부르며 호출하는 곳에서는 실제 값 형식을 알고 있기 때문에 형식 이름을 명시할 필요가 없습니다.
다음은 제네릭 메서드를 정의하여 사용한 예입니다. 여기서 제네릭 메서드는 전달받은 값을 출력한 후에 다시 그 값을 반환하는 메서드로 큰 의미는 없습니다. 단지 제네릭 메서드를 보여주기 위함입니다.
▷ 소스 3.2 제네릭 메서드
//제네릭 메서드 public class Program { static <T> T Foo(T value){ System.out.print(value); System.out.println(); return value; } public static void main(String[] args){ int i = 10; int re = Foo(i); System.out.println("re:"+re); } }
▷ 소스 3.2 실행 결과
10 re:10
제네릭을 표현할 때 특정한 규약을 따르는 형식만 사용할 때도 있습니다. 만약 정렬 메서드를 제공하려면 개체의 값을 비교할 수 있어야 할 것입니다. Java 라이브러리에는 Comparable 인터페이스를 정의하고 있고 비교할 수 있는 형식을 정의할 때 Comparable 인터페이스를 기반으로 구현 클래스를 정의하는 것을 권장합니다. 따라서 정렬 메서드를 제공할 때 최소한 Comparable 인터페이스를 기반의 구현 클래스로 한정할 필요가 있습니다. 주의할 점은 인터페이스일 때도 <가상 형식 이름 extends 특정 형식 이름> 형태로 표현해야 합니다.
다음은 정적 메서드 Sort의 입력 매개 변수로 Comparable 인터페이스로 한정한 제네릭 메서드를 구현한 예제입니다.
▷ 소스 3.3 Comparable 인터페이스로 한정한 제네릭 메서드 Sort
//Comparable 인터페이스로 한정한 제네릭 메서드 Sort public class Program { static <dt extends Comparable> void Sort(dt[] arr){ for(int i = arr.length; i>1; i--){ for(int j=1; j<i;j++){ if(arr[j].compareTo(arr[j-1])<0){ dt temp = arr[j-1]; arr[j-1] = arr[j]; arr[j] = temp; } } } } public static void main(String[] args){ String[] arr = {"홍길동", "강감찬", "을지문덕", "김구", "이순신"}; System.out.println("정렬 전"); for(int i = 0; i<arr.length;i++){ System.out.print(arr[i]+" "); } System.out.println(); Sort(arr); System.out.println("정렬 후"); for(int i = 0; i<arr.length;i++){ System.out.print(arr[i]+" "); } System.out.println(); } }
▷ 소스 3.3 실행 결과
정렬 전 홍길동 강감찬 을지문덕 김구 이순신 정렬 후 강감찬 김구 을지문덕 이순신 홍길동