‘컴파일 단계’에서 ‘잘못된 타입 사용될 수 있는 문제’제거 가능
자바5부터 새로 추가 !
컬렉션, 람다식(함수적 인터페이스), 스트림, NIO에서 널리 사용
제네릭을 모르면 API 도큐먼트 해석 어려우므로 학습 필요
- 컴파일 시 강핚 타입 체크 가능
• 실행 시 타입 에러가 나는 것 방지
• 컴파일 시에 미리 타입을 강하게 체크해서 에러 사젂 방지
타입변환 제거 가능
타입을 파라미터로 가지는 클래스와 인터페이스
선언 시 클래스 또는 인터페이스 이름 뒤에 ‚<>‛ 부호 붙임 ‚<>‛ 사이에는 타입 파라미터 위치
- 타입 파라미터
• 일반적으로 대문자 알파벳 핚 문자로 표현
• 개발 코드에서는 타입 파라미터 자리에 구체적인 타입을 지정해야
// <T> : generic data type (<-> specific)
// 1. class이름 바로 다음에 사용
// 2. T는 class Box에서 사용하는 data type임 (가상의 데이터 타입, 아직 결정되지 않은 데이터 타입)
// 3. T 대신에 어떠한 다른 글자를 사용해도 됨 (T : template)
// 4. 통상 1글자의 영문자를 사용
// 5. Box<T> : data type이면서 class 이름 으로 간주 가능
public class Box<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
// Box<String> : data type (T가 String이라는 의미임)
// => JVM이 실행할 때 Box<String>에 대한 class를 만들어 놓음
// => Box<Apple>, Box<Integer>도 JVM이 실행할 때 별도로 class를 만들어 놓음
// 단점 : class가 메모리에 여러개 만들어 짐
// 1. compile할 때 data type이 결정되어, 어떠한 data type을 사용하더라도 casting 없이 사용 가능
// 2. Generic type은 class뿐만 아니라, Interface에서도 사용 가능, method에 대해서도 사용 가능
public class BoxEx {
public static void main(String[] args) {
Box<String> b1 = new Box<String>();
b1.setT("hello");
String str = b1.getT();
Box<Apple> a1 = new Box<Apple>();
a1.setT(new Apple());
Apple apple = a1.getT();
Box<Integer> i1 = new Box<Integer>();
i1.setT(new Integer(10) );
Integer ii = i1.getT();
}
}
Generic type으로 T, M을 사용하겠다는 의미 (generic multi type)
- generic type뿐만 아니라, 일반 primitive type, 다른 class들도 사용 가능
- 대부분의 class들은 최대 3개까지의 generic type 사용
public class Product<T, M> {
private T kind;
private M model;
private int age;
public T getKind() {
return kind;
}
public void setKind(T kind) {
this.kind = kind;
}
public M getModel() {
return model;
}
public void setModel(M model) {
this.model = model;
}
}
class Tv {}
class Car {}
public class ProductEx {
public static void main(String[] args) {
Product<Tv, String> p1 = new Product<Tv, String>();
p1.setKind(new Tv());
p1.setModel("스마트TV");
Tv tv = p1.getKind();
String tvModel = p1.getModel();
Product<Car, String> p2 = new Product<Car, String>();
p2.setKind(new Car());
p2.setModel("디젤자동차");
Car car = p2.getKind();
String carModel = p2.getModel();
}
}
매개변수 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드
제네릭 메소드 선언 방법
• 리턴 타입 앞에 ‚<>‛ 기호를 추가하고 타입 파라미터 기술
• 타입 파라미터를 리턴 타입과 매개변수에 사용
public class Box<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
//<T> Box<T> boxing(T t)
//1. 처음에 나타난 <T> 의미 : method 안에서 T를 data type으로 사용
//2. Box<T> : method의 return type
public class Util {
public static <T> Box<T> boxing(T t) {
Box<T> box = new Box<T>();
box.setT(t);
return box;
}
}
public class GenericMethodEx {
public static void main(String[] args) {
Box<Integer> b1 = Util.<Integer>boxing(100); // auto boxing (primitive -> Integer)
int i1 = b1.getT();
System.out.println(i1);
Box<String> s1 = Util.<String>boxing("홍길동");
String name = s1.getT();
System.out.println(name);
}
}
// <T extends Number> :
// 1. T는 부모인 Number나 자식들인 Byte, Double, Float, Integer, Long, Short class만 가능하다는 의미
// 2. primitive type의 wrapper class들만 사용 가능
// Double.compare(a, b) method의 return 값
// 1) a < b이면, -1 return
// 2) a == b이면, 0 return
// 3) a > b이면, 1 return
public class Util {
public static <T extends Number> int compare(T t1, T t2) {
double d1 = t1.doubleValue();
double d2 = t2.doubleValue();
return Double.compare(d1, d2);
}
}
public class GenericExtendsEx {
public static void main(String[] args) {
// int result = Util.compare("a", "b");
int result = Util.compare(10, 20);
System.out.println(result);
result = Util.compare(4.5, 3);
System.out.println(result);
}
}
public class Person {
String name;
public Person(String name) {
this.name = name;
}
}
public class Student extends Person {
public Student(String name) {
super(name);
}
}
public class Worker extends Person {
public Worker(String name) {
super(name);
}
}
public class Course<T> {
private String name;
private T[] students;
String a;
public Course(String name, int capacity) {
this.name = name;
this.students = (T[]) new Object[capacity]; // new로 인스턴스 만들면 모든 참조타입의 field값은 null로 setting
// this.students = new T[capacity];
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T[] getStudents() {
return students;
}
public void setStudents(T[] students) {
this.students = students;
}
public void add(T t) {
for (int i=0; i<students.length; i++) {
if (students[i] == null) {
students[i] = t;
break;
}
}
}
}
// <?>, <? extends>, <? super > : method의 paramter나 return type에서만 사용 가능
public class RestrictedGenericEx {
public static void main(String[] args) {
Course<Person> personCourse = new Course<Person>("일반인과정", 5);
System.out.println(personCourse.a);
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);
registerCourseStudent(studentCourse);
registerCourseStudent(highStudentCourse);
registerCourseWorker(personCourse);
registerCourseWorker(workerCourse);
}
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()));
}
}
제네릭은 완전히 새롭게 배운거라 신기하면서도 뙈나 어려워서 강의가 끝나고 몇번 더 연습한 몇안되는 강의중 하나였다