제네릭(Generic)이란 결정되지 않은 타입을 파라미터로 처리하고 실제 사용할 때 파라미터를 구체적인 타입으로 대체시키는 기능이다.
public class Box {
public ? content
}
다양한 내용물을 저장할 Box 클래스를 선언하려고 한다. conent의 타입을 무엇으로 해야 할까?
모든 클래스의 루트 클래스인 Object를 넣으면 되지 않을까?
그럴듯하지만 객체의 내용물을 꺼낼 때 문제가 생긴다. content의 타입을 모르기 때문에 매번 instanceof 연산자로 타입을 조사해야 한다. 이는 좋은 방법이 아니다.
Box를 생성하기 전에 우리는 어떤 내용물을 넣을지 이미 알고 있다. 따라서 Box를 생성할 때 저장할 내용물의 타입을 미리 알려주면 Box는 content에 무엇이 대입되고, 읽을 때 어떤 타입으로 제공할지를 알게 된다. 이것이 제네릭이다.
public class Box<T> {
public T content
}
<T>는 T가 타입 파라미터임을 뜻하는 기호이다. <Integer>, <String> 등 타입을 넣을 수 있다.
T에 대체되는 타입은 클래스 및 인터페이스이어야 한다. int, char 같은 기본 타입은 들어갈 수 T가 될 수 없다.
T는 단지 이름이다. 어떤 알파벳을 써도 상관없다.
public class Main {
public static void main(String[] args) {
Box<String> box1 = new Box<>();
box1.content = "안녕하세요";
String str = box1.content;
System.out.println(str);
Box<Integer> box2 = new Box<>();
box2.content = 100;
int value = box2.content;
System.out.println(value);
}
}
타입 파라미터를 제한할 수 있다. 제한된 타입 파라미터를 bounded type parameter 라고 한다.
이제 Box의 T 타입은 Number와 Number의 자식, Number의 구현 관계에 있는 타입만 가능하다. 상위 타입은 클래스와 인터페이스가 들어올 수 있다.
public class Box<T extends Number>{
public T content;
}
T타입을 String으로 선언하면 오류가 발생한다.
제네릭 타입을 매개값이나 리턴 타입으로 사용할 때 타입 파라미터로 ?(와일드카드)를 사용할 수 있다.
<? extends 타입>와 <? super 타입>의 차이는 다음과 같다.
https://jojozhuang.github.io/programming/java-core-generics/
public class Applicant<T>{
public T kind;
public Applicant(T kind) {
this.kind = kind;
}
}
public class Person {
}
class Worker extends Person {
}
class Student extends Person {
}
class HighStudent extends Student {
}
class MiddleStudent extends Student {
}
public class Course {
public static void registerCourse(Applicant<?> applicant) {
System.out.println(applicant.kind.getClass().getSimpleName() + "이(가) Course를 등록함");
}
public static void regeisterCourseOnlyStudent(Applicant<? extends Student> applicant) {
System.out.println(applicant.kind.getClass().getSimpleName() + "이(가) CourseOnlyStudent를 등록함");
}
public static void registerCourseOnlyWorker(Applicant<? super Worker> applicant) {
System.out.println(applicant.kind.getClass().getSimpleName() + "이(가) CourseOnlyWorker를 등록함");
}
}
매개 타입이 맞지 않아 오류가 나는 모습이다.
주석처리 해주자.
public class Main {
public static void main(String[] args) {
// 모든 사람이 신청 가능
System.out.println("모든 사람이 신청 가능");
Course.registerCourse(new Applicant<Person>(new Person()));
Course.registerCourse(new Applicant<Worker>(new Worker()));
Course.registerCourse(new Applicant<Student>(new Student()));
Course.registerCourse(new Applicant<HighStudent>(new HighStudent()));
Course.registerCourse(new Applicant<MiddleStudent>(new MiddleStudent()));
System.out.println();
// 학생만 신청 가능
System.out.println("학생만 신청 가능");
// Course.regeisterCourseOnlyStudent(new Applicant<Person>(new Person()));
// Course.regeisterCourseOnlyStudent(new Applicant<Worker>(new Worker()));
Course.regeisterCourseOnlyStudent(new Applicant<Student>(new Student()));
Course.regeisterCourseOnlyStudent(new Applicant<HighStudent>(new HighStudent()));
Course.regeisterCourseOnlyStudent(new Applicant<MiddleStudent>(new MiddleStudent()));
System.out.println();
//직장인 및 일반인만 신청 가능
System.out.println("직장인 및 일반인만 신청 가능");
Course.registerCourseOnlyWorker(new Applicant<Person>(new Person()));
Course.registerCourseOnlyWorker(new Applicant<Worker>(new Worker()));
// Course.registerCourseOnlyWorker(new Applicant<Student>(new Student()));
// Course.registerCourseOnlyWorker(new Applicant<HighStudent>(new HighStudent()));
// Course.registerCourseOnlyWorker(new Applicant<MiddleStudent>(new MiddleStudent()));
}
}
// 실행결과
모든 사람이 신청 가능
Person이(가) Course를 등록함
Worker이(가) Course를 등록함
Student이(가) Course를 등록함
HighStudent이(가) Course를 등록함
MiddleStudent이(가) Course를 등록함
학생만 신청 가능
Student이(가) CourseOnlyStudent를 등록함
HighStudent이(가) CourseOnlyStudent를 등록함
MiddleStudent이(가) CourseOnlyStudent를 등록함
직장인 및 일반인만 신청 가능
Person이(가) CourseOnlyWorker를 등록함
Worker이(가) CourseOnlyWorker를 등록함