와일드카드 타입은 제네릭 사용에 있어서 다소 설명이 어렵게 느껴질 수 있는 부분입니다. 하지만 차근차근 설명을 읽어나가면 충분히 이해할 수 있는 내용이니, 이 아티클을 순서대로 인내심을 갖고 읽어 내려가시기를 바랍니다.
일단 일반적으로 코드에서 '?'를 와일드카드(wildcard)로 통칭합니다. 제네릭 타입 파라미터의 구체적인 값을 선언할 때, 이 와일드카드를 이용해서 세 가지 형태로 사용할 수 있게 됩니다. 일단 예제를 직접 보기 전까지는 쉽게 이해가 가지 않을 수 있으니 분류에 대해서만 먼저 확인해 봅시다.
· Unbounded Wildcards(제한 없음)
GenericName<?> genericName
모든 클래스나 인터페이스를 사용할 수 있다.
· Upper Bounded Wildcards(상위 클래스 제한)
GenericName<? extends UpperType> genericName
앞서 extends를 사용하는 제한된 타입 파라미터에서 배웠던 형태입니다. 이 경우에는 최상위 타입(UpperType)이나 해당 타입의 하위 타입만 사용이 가능합니다.
· Lower Bounded Wildcards(하위 클래스 제한)
GenericName<? super LowerType> genericName
상위 클래스 제한 형태의 반대로, 하위 타입이나 상위 타입이 올 수 있습니다.
위의 세 가지 사용 형태에 대해서, 설명만으로는 이해가 어려울 것입니다. 아래 예시를 통해서 다시 위의 설명들을 구체적으로 살펴보도록 하겠습니다.
우선, 제네릭 타입 Course를 기반으로 구현 코드를 작성할 예정인데, 이 전에 Person과 이하 하위 클래스들에 대한 구조를 먼저 확인하고 넘어가겠습니다.
일단 Course라는 클래스에서 코스의 이름과 학생의 타입을 정의하게 됩니다. 여기서 사용하게 될 학생의 타입이 위의 Person 클래스 구조입니다. 이 구조가 중요한 이유는, 우리가 위에서 배운 와일드카드 사용 방식이 적용되기 때문이죠.
이제 먼저 특정 레슨 코스에 대한 정보를 선언하는 Course 클래스를 정의해 보도록 하겠습니다.
public class Course<T> {
private String name; // 코스 이름
private T[] students; // 학생 타입
// 생성자
public Course(String name, int capacity) {
this.name = name;
students = (T[])(new Object[capacity]);
/* 타입 파라미터 기반 배열은 new T[n] 형태 사용 불가.
* (T[])(new Object[n]) 형태로 생성해야 한다.
*/
}
public String getName() { return name; }
public T[] getStudents() { return students; }
public void add(T t) {
for(int i=0; i < students.length; i++) {
if(students[i] == null) {
students[i] = t;
break;
}
}
}
}
public class Person {
public Person(String string) {
}
}
public class Worker extends Person {
public Worker(String string) {
super(string);
// TODO Auto-generated constructor stub
}
}
public class Student extends Person {
public Student(String string) {
super(string);
// TODO Auto-generated constructor stub
}
}
public class HighStudent extends Student {
public HighStudent(String string) {
super(string);
// TODO Auto-generated constructor stub
}
}
import java.util.Arrays;
public class ExampleMain {
// 모든 과정
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<Person>("일반인과정", 5);
personCourse.add(new Person("일반인"));
personCourse.add(new Worker("직장인"));
personCourse.add(new Student("학생"));
personCourse.add(new HighStudent("고등학생"));
Course<Worker> workerCourse = new Course<Worker>("직장인과정", 5);
workerCourse.add(new Worker("직장인"));
Course<Student> studentCourse = new Course<Student>("학생과정", 5);
studentCourse.add(new Student("학생"));
studentCourse.add(new HighStudent("고등학생"));
Course<HighStudent> highStudentCourse = new Course<HighStudent>("고등학생 과정", 5);
highStudentCourse.add(new HighStudent("고등학생"));
// 모든 과정 등록 가능
registerCourse(personCourse);
registerCourse(workerCourse);
registerCourse(studentCourse);
registerCourse(highStudentCourse);
System.out.println("-----");
// 학생 과정만 등록 가능. personCourse, workerCourse 등록 불가
registerCourseStudent(studentCourse);
registerCourseStudent(highStudentCourse);
System.out.println("-----");
// 직장인과 일반인 과정만 등록 가능. studentCourse, highStudentCourse 등록 불가
registerCourseWorker(personCourse);
registerCourseWorker(workerCourse);
}
}
'Programming > JAVA' 카테고리의 다른 글
람다식(1) - 람다식의 정의와 기본 문법 (0) | 2023.05.10 |
---|---|
제네릭(6) - 제네릭 타입의 상속과 구현 (0) | 2023.05.09 |
제네릭(4) - 제한된 타입 파라미터 선언 (0) | 2023.05.03 |
제네릭(3) - 제네릭 메서드 3 (0) | 2023.05.02 |
제네릭(3) - 제네릭 메서드 2 (0) | 2023.05.01 |