본문 바로가기

Programming/JAVA

제네릭(5) - 와일드카드 타입

java logo image

 

 

와일드카드 타입은 제네릭 사용에 있어서 다소 설명이 어렵게 느껴질 수 있는 부분입니다. 하지만 차근차근 설명을 읽어나가면 충분히 이해할 수 있는 내용이니, 이 아티클을 순서대로 인내심을 갖고 읽어 내려가시기를 바랍니다. 

 

일단 일반적으로 코드에서 '?'를 와일드카드(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);
	}
}