1. 제네릭란
- 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 파라미터를 결정되지 않은 타입으로 처리하고 실제 사용할 때(컴파일 시) 파라미터를 구체적인 타입으로 대체시키는 기능
- 우리가 컬렉션 프레임워크를 쓸 때 봤던 '<>'안에 타입을 지정하는 것이 제네릭이다.
- '<>'안에는 모든 알파벳이 들어갈 수 있지만 의미 있는 문자를 사용하는 것이 좋다.
- T : Type
- E : Element
- K : Key
- V : Value
// Box class
public class Box<T>{
public T content; // Box클래스에서 결정되지 않는 content의 타입을 T라는 타입 파라미터로 정의
void setContent(T content){
this.content = content;
}
T getContent(){
return content;
}
}
Box<String> box1 = new Box<>();
Box<Integer> box2 = new Box<>();
box1.content = "hello world";
box2.content = 123;
String content = box1.getContent(); // 형변환 필요없음
2. 제네릭 타입
- 제네릭 타입이란, 결정되지 않은 타입을 파라미터로 가지는 클래스와 인터페이스를 말한다.
- 선언부에 '<>'가 붙고 사이에 파라미터들이 들어간다.
public class ClassA<K, V, ...> {}
public interface InterfaceA<A, B, ...> {}
1) 사용 예
package generics;
public class Product <K, M>{
private K kind;
private M model;
public Product(K kind, M model) {
this.kind = kind;
this.model = model;
}
public K getKind() {
return kind;
}
public void setKind(K kind) {
this.kind = kind;
}
public M getModel() {
return model;
}
public void setModel(M model) {
this.model = model;
}
}
public class GenericEx {
public static void main(String[] args) {
Product<Tv, String> tv1 = new Product<>(new Tv(), "스마트TV-1");
System.out.println("kind : " + tv1.getKind() + ", model : " + tv1.getModel());
Product<Audio, String> audio1 = new Product<>(new Audio(), "스탠드형 오디오1");
System.out.println("kind : " + audio1.getKind() + ", model : " + audio1.getModel());
}
}
2) 타입 파라미터의 비교
- 타입 파라미터는 Object타입으로 간주되기 때문에 Object가 가지고 있는 기본 메서드들을 호출하여 사용할 수 있다.
public class Box<T>{
public T content;
public boolean equals(Box<T> obj) {
return content.equals(obj.content);
}
}
3. 제네릭 메서드
- 제네릭 메서드란, 타입 파라미터를 가지고 있는 메서드를 말한다. -> 메서드의 선언부에 제네릭 타입을 선언하면 제네릭 메서드다.
public <A, B, ...> [리턴타입] [메서드명](매개변수.. ) { ... }
public class Box<T>{
public T content;
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
public boolean equals(Box<T> obj) {
return content.equals(obj.content);
}
public static <T> Box<T> boxing(T t) {
// 타입 파라미터를 <T>로, 리턴타입은 T를 가지는 Box객체
Box<T> box = new Box<>();
box.setContent(t);
return box;
}
}
public class GenericEx {
public static void main(String[] args) {
Box<Integer> box1 = Box.boxing(100);
Integer box1Content = box1.getContent();
System.out.println("box1Content : " + box1Content);
Box<String> box2 = Box.boxing("hello world");
String box2Content = box2.getContent();
System.out.println("box2Content : " + box2Content);
}
}
4. 제한된 타입 파라미터
- 숫자를 연산하는 제네릭 메서드와 같이 타입 파라미터를 대체하는 구체적인 타입을 제한해야 할 때가 있다.
- 특정 타입과 그 자식 또는 구현 관계에 있는 타입만 대체할 수 있는 타입 파라미터를 제한된 타입 파라미터라고 한다.
public <T extends 부모타입> [리턴타입] [메서드명] (매개변수, ...) { ... }
public <T extends Number> boolean compare(T t1, T t2){
double v1 = t1.doubleValue();
double v2 = t2.doubleValue();
return v1 == v2;
}
5. 와일드카드
- 와일드카드(?)는 범위에 있는 모든 타입으로 대체할 수 있다는 표시이다.
- 제네릭 타입을 매개변수나 리턴 타입을 사용할 때 와일드카드(?)를 사용할 수 있다.
- <? extends T> : T와 그 자손들만 가능
- <? super T> : T와 그 조상들만 가능
- <?> : 제한 없음
public class Person {}
public class Worker extends Person{}
public class Student extends Person{}
public class HighSchoolStudent extends Student{}
public class Applicant <T>{
public T kind;
public Applicant(T kind) {
this.kind = kind;
}
}
public class Course {
public static void registerCourseA(Applicant<?> applicant) {
System.out.println(applicant.kind.getClass().getSimpleName() + "가 A코스 등록");
}
public static void registerCourseB(Applicant<? extends Student> applicant) {
System.out.println(applicant.kind.getClass().getSimpleName() + "가 B코스 등록");
}
public static void registerCourseC(Applicant<? super Worker> applicant) {
System.out.println(applicant.kind.getClass().getSimpleName() + "가 C코스 등록");
}
}
public class GenericEx {
public static void main(String[] args) {
// courseA : 사람이면 모두 신청 가능
Course.registerCourseA(new Applicant<Person>(new Person()));
Course.registerCourseA(new Applicant<Worker>(new Worker()));
Course.registerCourseA(new Applicant<Student>(new Student()));
Course.registerCourseA(new Applicant<HighSchoolStudent>(new HighSchoolStudent()));
// courseB : Student만 신청 가능
// Course.registerCourseB(new Applicant<Person>(new Person())); // 안됨
// Course.registerCourseB(new Applicant<Worker>(new Worker())); // 안됨
Course.registerCourseB(new Applicant<Student>(new Student()));
Course.registerCourseB(new Applicant<HighSchoolStudent>(new HighSchoolStudent()));
// courseC : 직장인, 일반인만 신청가능
Course.registerCourseC(new Applicant<Person>(new Person()));
Course.registerCourseC(new Applicant<Worker>(new Worker()));
// Course.registerCourseC(new Applicant<Student>(new Student())); // 안됨
// Course.registerCourseC(new Applicant<HighSchoolStudent>(new HighSchoolStudent())); // 안됨
}
}