본문 바로가기

Java 공부

13장 제네릭(Generic)(5)_와일드 카드 타입


API Document의 매개변수나 리턴타입에서 자주 볼 수 있는 와일드 카드 타입에 대해서 알아본다.


와일드카드(?)타입?

- 이미 선언되어 있는 제네릭 타입을 매개변수나 리턴타입으로 사용할 때, 타입 파라미터를 제한할 목적으로 사용한다.

** <T extends 상위타입> 은  제네릭 타입을 선언할 때 타입 파라미터를 제한하기 위해 쓴다.

- 와일드카드 타입의 세가지 형태

1. 제네릭타입<?> : Unbounded Wildcards (제한없음)

2. 제네릭타입<? extends 상위타입> : Upper Bounded Wildcards (상위 클래스 제한)

3. 제네릭타입<? super 하위타입> :  Lower Bounded Wildcards (하위 클래스 제한)


예를 들어,      (상위)  A >B >C >D >E  (하위)     일 경우

 <? extends C> : C, D, E 가능

 <? super C>  : A, B, C 가능



예제>> 

제네릭 타입을 가지는 Course 클래스

과정을 수강할 수 있는 대상이 T타입에 온다 ex) 일반인,학생,직장인 등

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 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;

}

}

}

}


--> private T[] students;

T타입의 배열이므로 T자리에는 학생, 일반인, 직장인 등 다양한 타입의 클래스가 들어올 수 있다.

따라서 어떤 클래스가 들어오느냐에 따라 다양한 타입의 배열이 될 수 있다!

--> capacity만큼의 크기를 가지는 T타입의 배열을 생성하려면? 

students = new T[capacity];  이라고 할 수 없다.  T가 어떤 타입인지 결정되지 않았기 때문에 T타입의 배열을 생성할 수 없다.

따라서 먼저 Object타입의 배열을 생성해주고 후에 T타입으로 강제 타입 변환을 시켜줘야 한다.

students = (T[])new Object[capacity];

--> public void add(T t) { ... }

과정을 등록했다가 중간에 취소하고 나가는 학생이 발생할 것을 대비해 빈 인덱스부터 채우기 위해서 메소드를 만든다.




수강 대상 클래스 : Person

public class Person {

private String name;  //이름

public Person(String name) {

this.name = name;

}

public String getName() { return name; }

@Override

public String toString() {

return name;

}

}


--> public String toString() {  return name; }

     toString()메소드를 재정의하여 Person을 출력하면 name을 바로 출력할 수 있도록 한다.




수강 대상 클래스 : Student (Person 상속)

public class Student extends Person {

public Student(String name) {

super(name);

}

}


--> public Student(String name) { super(name); }

외부에서 name을 받아서 받은 name을 Person의 생성자에 넣어준다.

부모 생성자(Person)가 현재 name을 매개변수로 받아서 가지고 있는 name에 넣는 생성자 1개만 가지기 때문에 (기본 생성자 존재 X)

Person을 상속받는 Student클래스도 부모생성자를 호출해주어야 한다.



수강 대상 클래스 : Worker (Person 상속)

public class Worker extends Person {

public Worker(String name) {

super(name);

}

}



수강 대상 클래스 : HighStudent(Student 상속)

public class HighStudent extends Student{

public HighStudent(String name) {

super(name);

}

}



실행클래스 : WildCardExample

public class WildCardExample {


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 Student> 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 Person("직장인"));

personCourse.add(new Person("학생"));

personCourse.add(new Person("고등학생"));

Course<Worker> workerCourse = new Course<>("직장인 과정",5);

workerCourse.add(new Worker("직장인"));

Course<Student> studentCourse = new Course<>("학생 과정",5);

studentCourse.add(new Student("학생"));

studentCourse.add(new HighStudent("고등학생"));     //(O) 

Course<HighStudent> highstudentCourse = new Course<>("고등학생 과정",5);

//highstudentCourse.add(new Student("학생")); (X)

highstudentCourse.add(new HighStudent("고등학생"));


registerCourse(personCourse);

registerCourse(workerCourse);

registerCourse(studentCourse);

registerCourse(highstudentCourse);

System.out.println();

//registerCourseStudent(personCourse);  (X)

//registerCourseStudent(workerCourse); (X)

registerCourseStudent(studentCourse);

registerCourseStudent(highstudentCourse);

System.out.println();

registerCourseWorker(personCourse);

registerCourseWorker(workerCourse);

//registerCourseWorker(studentCourse); (X)

//registerCourseWorker(highstudentCourse); (X)

System.out.println();

}


}


실행결과

일반인 과정 수강생: [일반인, 직장인, 학생, 고등학생, null]

직장인 과정 수강생: [직장인, null, null, null, null]

학생 과정 수강생: [학생, 고등학생, null, null, null]

고등학생 과정 수강생: [고등학생, null, null, null, null]


학생 과정 수강생: [학생, 고등학생, null, null, null]

고등학생 과정 수강생: [고등학생, null, null, null, null]


일반인 과정 수강생: [일반인, 직장인, 학생, 고등학생, null]

직장인 과정 수강생: [직장인, null, null, null, null]


--> Course<?> course  : 제네릭 타입 Course에 사용할 수 있는 타입 파라미터의 구체적인 클래스가 제한없이 무엇이든 올 수 있다.

Course<? extends Student> course : Student가 상위타입이므로 Student와 그 하위 객체만 올 수 있다.

Course<? super Worker> course : Worker가 하위타입이므로 Worker와 그 상위 객체만 올 수 있다.

extends : 상위타입 제한  /   super : 하위타입 제한

--> Course<Person> personCourse = new Course<>("일반인 과정",5); 

Course<Person> personCourse = new Course<Person>("일반인 과정",5);

두 문장은 같다. <Person>은 생략 가능하다.

-->  studentCourse.add(new HighStudent("고등학생"));     // (O)

Student가 부모클래스이므로 하위클래스인 HighStudent클래스는 올 수 있다.

--> highstudentCourse.add(new Student("학생"));          // (X)

HighStudent가 하위클래스이므로 부모클래스는 올 수 없다.






본 포스팅은 이것은 자바다 책을 참고하여 작성하였습니다.