-
[Java] 제네릭(generic) - 제한된 타입 파라미터, 와일드카드 타입CSE/Java 2015. 9. 20. 11:34
제네릭은 여러 절로 구성되어 있습니다.
제한된 타입 파라미터(<T extends 최상위 타입>)
타입 파라미터에 지정되는 구체적인 타입을 제한할 필요가 종종 있습니다.
예를 들어 숫자를 연산하는 제네릭 메소드는 매개값으로 Number 타입 또는 하위 클래스 타입(Byte, Short, Integer, Long, Double)의 인스턴스만 가져야 합니다.
이것이 제한된 타입 파라미터(bounded type parameter)가 필요한 이유입니다.
제한된 타입 파라미터를 선언하려면 타입 파라미터 뒤에 extends 키워드를 붙이고 상위 타입을 명시하면 됩니다.
상위 타입은 클래스뿐만 아니라 인터페이스도 가능합니다.
인터페이스라고 해서 implements를 사용하지 않습니다.
12public <T extends 상위타입> 리턴타입 메소드 (매개변수, ...) { ... }cs 타입 파라미터에 지정되는 구체적인 타입은 상위 타입이거나 상위 타입의 하위 클래스 또는 구현 클래스만 가능합니다.
주의할 점은 메소드의 중괄호 {} 안에서 타입 파라미터 변수로 사용 가능한 것은 상위 타입의 멤버(필드, 메소드)로 제한됩니다.
하위 타입에만 있는 필드와 메소드는 사용할 수 없습니다.
다음은 숫자 타입만 구체적인 타입으로 갖는 제네릭 메소드 compare() 입니다.
두 개의 숫자 타입을 매개값으로 받아 차이를 리턴합니다.
1234567public <T extends Number> int compare(T t1, T t2) {double v1 = t1.doubleValue();double v2 = t2.doubleValue();return Double.compare(v1, v2);}cs doubleValue() 메소드는 Number 클래스에 정의되어 있는 메소드로 숫자를 double 타입으로 변환합니다.
Double.compare() 메소드는 첫 번째 매개값이 작으면 -1을, 같으면 0을, 크면 1을 리턴합니다.
* Util.java
123456789101112package generic;public class Util {public static <T extends Number> int compare(T t1, T t2) {double v1 = t1.doubleValue();double v2 = t2.doubleValue();return Double.compare(v1, v2);}}cs * BoundedTypeParameterExam.java
123456789101112131415package generic;public class BoundedTypeParameterExam {public static void main(String[] args) {int result1 = Util.compare(10, 20);System.out.println(result1);int result2 = Util.compare(4.5, 3);System.out.println(result2);}}cs 와일드카드 타입(<?>, <? extends ...>, <? super ...>)
코드에서 ?를 일반적으로 와일드카드(wildcard)라고 부릅니다.
제네릭 타입을 매개값이나 리턴 타입으로 사용할 때 구체적인 타입 대신 와일드 카드를 다음과 같이 세 가지 형태로 사용할 수 있습니다.
- 제네릭 타입<?>: Unbounded Wildcards(제한 없음)
타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있습니다.
- 제네릭 타입<? extends 상위 타입>: Upper Bounded Wildcards(상위 클래스 제한)
타입 파라미터를 대치하는 구체적인 타입으로 상위 타입이나 하위 타입이 올 수 있습니다.
- 제네릭 타입<? Super 하위 타입>: Lower Bounded Wildcards(하위 클래스 제한)
타입 파라미터를 대치하는 구체적인 타입으로 하위 타입이나 상위타입이 올 수 있습니다.
다음 코드를 보면서 이해해보도록 합시다. 제네릭 타입 Course는 과정 클래스로 과정 이름과 수강생 이름을 저장할 수 있는 배열을 가지고 있습니다.타입 파라미터 T가 적용된 곳은 수강생 타입 부분입니다.* Course.java1234567891011121314151617181920212223242526272829303132333435363738package generic;public class Course<T> {private String name;private T[] students;public Course(String name, int capacity) {this.name = name;students = (T[]) (new Object[capacity]);}public String getName() {return name;}public void setName(String name) {this.name = name;}public T[] getStudents() {return students;}public void setStudents(T[] students) {this.students = students;}public void add(T t) {for (int i = 0; i < students.length; i++) {if (students[i] == null) {students[i] = t;break;}}}}cs 수강생이 될 수 있는 타입은 다음 5가지 클래스라고 가정합시다. Person의 하위 클래스로 Worker와 Student가 있고, Student의 하위 클래스로 HighStudent가 있습니다.
- Course<?>
수강생은 모든 타입(Person, Worker, Student, HighStudent, Dog>이 될 수 있습니다.
- Course<? extends Student>
수강생은 Student와 HighStudent만 될 수 있습니다.
- Course<? super Worker>
수강생은 Worker와 Person만 될 수 있습니다.
다음 예제는 registerCourseXXX() 메소드의 매개값으로 와일드카드 타입을 사용했습니다.
registerCourse()는 모든 수강생이 들을 수 있는 과정을 등록하고, registerCourseStudent()는 학생만 들을 수 있는 과정을 등록합니다.
그리고 registerCourseWorker는 직장인만 들을 수 있는 과정을 등록합니다.
* wildCardExam.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455package generic;import java.util.Arrays;public class wildCardExam {public static void registerCourse(Course<?> course) {System.out.println(course.getName() + " 수강생: " + Arrays.toString(course.getStudents()));}public static void registerCourseStudent(Course<? extends Student> course) {System.out.println(course.getName() + " 수강생: " + Arrays.toString(course.getStudents()));}public static void registerCourseWorker(Course<? super Worker> course) {System.out.println(course.getName() + " 수강생: " + Arrays.toString(course.getStudents()));}public static void main(String[] args) {Course<Person> personCourse = new Course<>("일반인 과정", 5);personCourse.add(new Person("일반인"));personCourse.add(new Worker("직장인"));personCourse.add(new Student("직장인"));personCourse.add(new HighStudent("직장인"));Course<Worker> workCourse = new Course<>("직장인 과정", 5);workCourse.add(new Worker("직장인"));Course<Student> studentCourse = new Course<>("학생 과정", 5);studentCourse.add(new Student("학생"));studentCourse.add(new HighStudent("고등학생"));Course<HighStudent> highStudentCourse = new Course<>("고등학생 과정", 5);highStudentCourse.add(new HighStudent("고등학생"));registerCourse(personCourse);registerCourse(workCourse);registerCourse(studentCourse);registerCourse(highStudentCourse);System.out.println();registerCourseStudent(studentCourse);registerCourseStudent(highStudentCourse);System.out.println();registerCourseWorker(workCourse);registerCourseWorker(personCourse);}}cs * 이 포스트은 서적 '이것이 자바다' 를 참고하여 작성한 포스트입니다.
'CSE > Java' 카테고리의 다른 글
[Java] 람다식 - 기본 문법, 타겟 타입과 함수적 인터페이스 (0) 2015.09.20 [Java] 람다식 - Intro (0) 2015.09.20 [Java] 제네릭(generic) - 제네릭 타입의 상속과 구현 (0) 2015.09.20 [Java] 제네릭(generic) - 제네릭 메소드 (0) 2015.09.20 [Java] 제네릭(generic) - 멀티 타입 파라미터 (0) 2015.09.20 [Java] 제네릭(Generic) - 제네릭 타입 (1) 2015.09.20