java 재네릭
재네릭은 클래스 내부에서 사용할 데이터타입을 외부에서 지정하는 기법을 의미한다.
데이터타입을 외부에서 지정하면 장점뭘까?
메소드 선언에 사용되는 파라미터 형식처럼, 타입 파라미터는 다른 입력값을 같은 코드를 재사용 할 수 있는 방법을 제시한다.
다른점은 형식 파라미터는 값이 아니라 입력이다.
타입파라미터는 타입이 입력!
제네릭 없이 캐스팅이 필요한 코드
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
제네릭을 사용하여 코드 작성시,캐스팅이 필요 없어진다.
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);
제네릭을 사용함으로써 일반적인 알고리즘을 구현하도록 지원한다.
generics를 사용하면서 다양한 타입들의 콜렉션을 사용할 수 있고 더 안정적이고 읽기 쉬운 코드를 일반적인 알고리즘으로 구현할 수 있다.
좀더 자세히 알아보자.
아래의 코드를 살펴보자.
class Person<T>{
public T info;
}
public class GenericDemo{
public static void main(String[] arge){
Person<String> p1 = new Person<String>();
Person<StringBuilder> p2 = new Person<StringBuilder>();
}
}
데이터 타입 결과는
p1.info = String
p2.info = StringBuilder
각각의 인스턴스를 생성할 때 사용한 <> 사이에 어떤 데이터 타입을 사용했느냐에 달려있다.
클래스 선언부
public
T info;
클래스 필드 info의 데이터 타입은 T로 되어있다. 실제로 T라는 데티어 타입은 존재하지 않는다.
이값은 아래 코드의 T에서 정해진다.
제너릭 클래스 선언부
class
Person<T>{
제네릭 클래스의 인스턴스 생성부
Person<String> p1 =
new
Person<String>();
제네릭 클래스의 인스턴스 생성시 <String> 으로 지정하면서 제네릭 T info는 String info 가 된다.
재네릭 사용하는 이유
class StudentInfo{
public int grade;
StudentInfo(int grade){ this.grade = grade; }
}
class StudentPerson{
public StudentInfo info;
StudentPerson(StudentInfo info){ this.info = info; }
}
class EmployeeInfo{
public int rank;
EmployeeInfo(int rank){ this.rank = rank; }
}
class EmployeePerson{
public EmployeeInfo info;
EmployeePerson(EmployeeInfo info){ this.info = info; }
}
public class GenericDemo {
public static void main(String[] args) {
StudentInfo si = new StudentInfo(2);
StudentPerson sp = new StudentPerson(si);
System.out.println(sp.info.grade); // 2
EmployeeInfo ei = new EmployeeInfo(1);
EmployeePerson ep = new EmployeePerson(ei);
System.out.println(ep.info.rank); // 1
}
}
위 코드를 보면 StudentPerson와 EmployeePerson가 중복되는 클래스이다.
f(x) = x 함수 클래스로 x 매개변수를 제네릭 클래스로 구현하여 StudentPerson와 EmployeePerson 클래스중복을 제거해보자.
class StudentInfo{
public int grade;
StudentInfo(int grade){ this.grade = grade; }
}
class EmployeeInfo{
public int rank;
EmployeeInfo(int rank){ this.rank = rank; }
}
class Person<T>{
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));
EmployeeInfo ei1 = p1.info;
System.out.println(ei1.rank); // 성공
Person<String> p2 = new Person<String>("부장");
String ei2 = p2.info;
System.out.println(ei2.rank); // 컴파일 실패
}
}
기본 데이터 타입과 제네릭
제네릭은 참조 데이터 타입에 대해서만 사용할 수 있다. 기본 데이터 타입에서는 사용할 수 없다. 따라서 아래 코드의 int는 오류난다.
int . char, double ..등등 은 기본데이터로 사용할 수 없다. int 대신 integer 로 대신하여 사용할 수 있다.
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, int> p1 = new Person<EmployeeInfo, int>(new EmployeeInfo(1), 1);
}
}
복수 제네릭
클래스 내에서 여러개의 제네릭을 필요로 하는 경우 아래의 코드를 참조하자.
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) {
EmployeeInfo e = new EmployeeInfo(1);
Integer i = new Integer(10);
Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);
System.out.println(p1.id.intValue());
}
}
재네릭 메소드 사용
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 <U> void printInfo(U info){
System.out.println(info);
}
}
public class GenericDemo {
public static void main(String[] args) {
EmployeeInfo e = new EmployeeInfo(1);
Integer i = new Integer(10);
Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);
p1.<EmployeeInfo>printInfo(e);
p1.printInfo(e);
}
}
재네릭 인스턴스 생략 가능하다. 아래와 같이 p1,p2 모두 정상적으로 생성된다.
Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);
Person p2 =
new
Person(e, i);
extends 로 재네릭으로 올 수 있는 데이터 타입을 특정 클래스의 자식으로 제한할 수 있다.
extends는 상속 관계인 클래스 뿐만 아니라 구현(implements)의 관계에서도 사용할 수 있다.
interface Info{
int getLevel();
}
class EmployeeInfo implements Info{
public int rank;
EmployeeInfo(int rank){ this.rank = rank; }
public int getLevel(){
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 p1 = new Person(new EmployeeInfo(1));
Person<String> p2 = new Person<String>("부장");
}
}
제네릭에 대해 기본적인 개념만 살펴보았다.