이번시간은 자바5부터 도입된 Generic(제네릭)타입에 대해서 알아보자.
우리가 생각하는 데이터타입이라하면, int, char, double...같은 기본데이터타입이 생각난다.
그렇다면 Generic(제네릭)이란...
이런 데이터타입을 아직 명시하지 않은 상태라고 생각하면된다.
즉 클래스안에서 사용하는 데이터타입을 미리 정의하지 않고, 클래스를 인스턴스화 하는 시점에
데이터타입을 지정해주는 방식을 가능케한다.
제네릭 구현 방법은 아래와 같이 사용할 수 있다.
public class 클래스명<T>{ ... }
public interface 인터페이스명<T>{ ... }
이제 코드를 통해 알아보자.
자바의 모든 클래스의 최상위 조상(부모) 클래스는 Object 클래스이다.
그렇다면, 어느타입이든 Object클래스는 기본데이터타입을 객체화시킨 wrapperType의 객체를 대입받을 수 있다.
말이 어려우니 아래 예제를 보자.
Box.java
public class Box { private Object object; public Object get(){return object;} public void set(Object object){this.object = object;} }
Main.java
public class Main { public static void main(String[] args) { Box box = new Box(); box.set("제네릭"); String str = box.get();//(String)box.get(); 캐스팅필요 System.out.println(str); } }
이렇게 했을 경우 Main.java 7번째 줄에서 컴파일 에러가 뜰것이다.
즉 Object를 구체적인 데이터타입으로 명시화해줘야한다.
즉 String str = (String)box.get(); 같이 캐스팅을 해줘야 에러가 나지않는다.
여기서 Generic(제네릭)을 쓰는 첫번째 이유가 나온다. 매번 데이터타입을 강제로 캐스팅을 한다면,
프로그램 성능저하가 발생되기 때문에 모든 타입을 Object로 받아주는 것 보다. 인스턴스화시 타입을 지정해줘서
위와 같은 불필요한 강제타입변환을 막을 수 있다.
위의 코드를 제네릭으로 바꿔보자.
Box.java
public class Box{ private T t; public T get(){return t;} public void set(T t){this.t = t;} }
Main.java
public class Main { public static void main(String[] args) { Boxbox = new Box (); box.set("제네릭"); String str = box.get(); System.out.println(str); } }
강제적인 타입변환이 발생하지 않는다. 즉 인스턴스화시
Box<String> box = new Box<String>(); String으로 타입을 지정해주었다.
이렇게함으로써 클래스 내부적으로 T 제네릭타입은 String으로 자동 재구성된다.
(위 코드에서 </t>,</string></string>은 무시해도된다;; 티스토리 이클립스 코드스킨 버그다;.)
그 다음 생각해 볼것이 만약 Generic(제네릭)이 없다면...
우리는 필요한 데이터타입에 맞는 매번 새로운 클래스 또는 메서드를 작성해야한다...
box.set(데이터타입); 이라했을 때
매번 box.set()메서드에서 들어갈 매개변수 데이터 타입에 맞는 메서드를 구현할텐가??
생각만해도 개발자가 제일 싫어하는 반복코드가 발생한다.
여기서 Generic(제네릭)이 또 필요한 이유가 나온다. 반복적인 코드 절약! 코드 재사용성! 이라는 이점을 취할 수 있다.
또 한가지 이점이 있다. 아래 생활코딩에서 발췌해온 코드를 보자.
*생활코딩에서 발췌
package org.opentutorials.javatutorials.generic; 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 T info; Person(T info){ this.info = info; } } public class GenericDemo { public static void main(String[] args) { Person p1 = new Person (new EmployeeInfo(1)); EmployeeInfo ei1 = p1.info; System.out.println(ei1.rank); // 성공 Person p2 = new Person ("부장"); String ei2 = p2.info; System.out.println(ei2.rank); // 컴파일 실패 } }
(제네릭이 도입되기 전) 기존에 우리가 object로 작성한 코드는 모든 데이터 타입을 받아주기 위한 취지로 object를 썼지만, 그렇게 하면 개발자가 우리가 의도한 데이터타입을 사용하지 않고 위 코드처럼 우리가 의도하는 인스턴스 객체타입이 들어가야할 자리에 "부장" 이라는 String 타입이 들어가는 것을 막을 수 없다.
에러가 나는 이유는 ei2는 EmployeeInfo 타입이 아닌 String타입으로 매핑 됬기 때문에 우리가 임의로 만든 EmployeeInfo의 데이터타입 rank를 출력할 수 없다.
하지만 Generic(제네릭)을 사용하면 위와 같은 잠재성이 있는 에러를 런타임 사전에 에러체크 할 수 있다.
컴파일시 타입오류를 발생시키는 것이다.
정리하면 object 인 경우 우리가 의도한 데이터타입이 아닌 경우 컴파일 에러를 발생시키지 않아 잠재적 문제를 발생시키지만, Generic(제네릭)은 우리가 의도한 데이터타입이 나닌 경우 미리 컴파일 에러를 발생시켜 사전에 문제를 해결 할 수 있게 해준다.
이로써 3번째 Generic(제네릭)의 장점인
Generic(제네릭) 은 컴파일시 타입오류를 체크하여 사전에 엄격한 타입체크를 가능케한다. 는 것을 도출 할 수 있다.
자 정리하면. Generic(제네릭)이 우리에게 가져다 주는 장점
1. 프로그램 성능저하를 유발하는 캐스팅(강제 데이터타입 변환)을 제거한다.
2. 코드절약 및 코드 재사용성을 증진시켜 유지보수를 편하게 한다.
3. 컴파일시 타입오류를 체크하여, 사전에 엄격한 데이터타입 체크를 가능케한다.
제네릭을 사실 처음 공부해본 입장에선 많이 공감되지 않을 수 있다...
왜냐...가장 늦게 추가된 자바 기능이기 때문에.. 무슨말이냐면 기존에 자바를 써왓던 사람들로 하여금 이러저러한
불만과 해결을 요구하는 목소리가 커졌고 이에 부흥하기 위해 Generic(제네릭)이라는 기능이 탄생한 것이다.
따라서 자바를 이제 시작했거나 신입개발자들에겐 Generic(제네릭)의 필요성과 Generic(제네릭)이 가져다 주는 이점에 대해 크게 감탄할 수 없을 지도 모른다. 하지만 Generic(제네릭)은 java 라이브러리에 많이 쓰이기 때문에 코드를 보기위해서라도 이해하고 있어야하며, 실무에서 Generic(제네릭)을 쓰는 경우가 있기 때문에 반드시 알아두어야 한다.
다음 시간에는 제네릭의 멀티타입, 제네릭메서드, 제네릭 최상위타입, 와일드카드 등이 무엇인지 알아보도록 하겠다.
'프로그래밍 > Java' 카테고리의 다른 글
[Java] 제네릭 메서드(Generic Method) (1) | 2017.07.15 |
---|---|
[Java] 제네릭(Generic) 멀티 타입 파라미터 (0) | 2017.07.13 |
[Java] Thread Pool(스레드 풀) (11) | 2017.07.07 |
[Java] multiThread name - 스레드 이름 (0) | 2017.07.02 |
[Java] multi thread 구현 02 (0) | 2017.07.02 |
댓글