클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법
class Person<T> {
public T info;
}
Person<String> p1 = new Person<String>();
//p1.info -> String 타입
Person<StringBuilder> p2 = new Person<StringBuilder>();
//p2.info -> StringBuilder 타입
class StudentInfo {
public int grade;
StudentInfo(int grade) {
this.grade = grade;
}
}
class EmployeeInfo {
public int rank;
EmployeeInfo(int rank) {
this.rank = rank;
}
}
class Person {
public Object info;
Person (Object info) {
this.info = info;
}
}
public class GenericDemo {
public static void main(String[] args) {
Person p1 = new Person("부장");
//info에 StudentInfo나 EmployeeInfo를 넣고 싶은 것이지만 컴파일 상에서 아무 문제가 없음
EmployeeInfo ei = (EmployeeInfo) p1.info;
System.out.println(ei.rank);
//그렇다고 Student, Employee 두 개의 클래스를 구현하기엔 코드의 중복!!!이 발생한다
}
}
복수의 제네릭을 사용할 때는 <T, S>
와 같은 형식을 사용한다!
T와 S 대신 어떤 문자를 사용해도 된다
class EmployeeInfo {
public int rank;
EmployeeInfo(int rank) {
this.rank = rank;
}
}
class Person<T, S> {
public T info;
public S id;
Person(T info, S id) {
this.info = info;
this.id = id;
}
}
public class GenericDemo {
public static void main(String[] args) {
Person<EmployeeInfo, Integer> p1 = new Person<>(new EmployeeInfo(1), 1);
}
}
제네릭은 참조 데이터 타입
에 대해서만 사용할 수 있다!
기본 데이터 타입을 사용해야 할 경우에는 -> Wrapper 클래스
를 사용
import java.util.ArrayList;
public class GenericDemo {
public static void main(String[] args) {
ArrayList<int> arr = new ArrayList<>(); //오류 발생
ArrayList<Integer> arr = new ArrayList<>(); //문제 없음
}
}
Person을 생성할 때 들어오는 e, i의 타입
을 통해 Generic으로 들어오는 T와 S의 타입을 알 수 있다
public class GenericDemo {
public static void main(String[] args) {
EmployeeInfo e = new EmployeeInfo(1);
Integer i = new Integer(10);
Person p1 = new Person(e,i);
//원래는 Person<EmployeeInfo, Integer> 이라고 명시 해줘야 함
}
}
필드가 아닌 메소드
에도 Generic을 사용할 수 있다
class Person <T, S> {
//제네릭을 메소드에 적용하기
public <U> void printInfo(U info) {
System.out.println(info);
}
}
선언한 제네릭에 아무 타입이 오는 것이 싫다!
최소한의 범위를 정해주고 싶다면? 제한
을 걸어둘 수 있다!
interface info {
int getLevel();
}
class EmployeeInfo implements Info {
public int rank;
EmployeeInfo(int rank) {
this.rank = rank;
}
public int getRank() {
return this.rank;
}
}
class Person<T extends Info> {
public T info;
Person(T info) {
this.info = info;
}
}
public class GenericDemo {
public static void main(String[] args) {
//정상적으로 작동
Person<EmployeeInfo> p1 = new Person<EmployeeInfo>(new EmployeeInfo(1));
//에러 발생
Person<String> p2 = new Person<String>("부장");
}
}
class Person<T super Integer> {
public T level;
Person(T level) {
this.level = level;
}
}
public class GenericDemo {
public static void main(String[] args) {
//정상적으로 작동
Person<Integer> p1 = new Person<>(1);
Person<Object> p1 = new Person<>(Object o);
//에러 발생
Person<String> p2 = new Person<>();
}
}
<?>
는 와일드 카드로, <? extends Object>
와 마찬가지이다class Sample<T> {
public void method() {
T t = new T(); //생성 안 됨
}
}
class Employee<T> {
private String name;
private int age = 0;
public static T addAge(int n) {
//static 메서드의 반환 타입으로 사용 불가
//왜냐하면 static은 제네릭 객체가 생성되기도 전에 이미 자료 타입이 정해져 있어야 하기 때문
}
}
class Sample<T> { }
public class Main {
public static void main(String[] args) {
Sample<Integer>[] arr1 = new Sample<>[10];
//생성 불가능
//제네릭 클래스 자체를 배열로 만들 수는 없다
//하지만? 제네릭 타입의 배열 선언은 허용된다
Sample<Integer>[] arr2 = new Sample[10];
//제네릭 타입을 생략해도 위에서 이미 정의했기 때문에 Integer가 자동으로 추론됨
arr2[0] = new Sample<Integer>();
arr2[1] = new Sample<>();
//Integer가 아닌 타입은 저장 불가능
arr[2] = new Sample<String>();
}
}