제네릭 개념 알기

JAVA/JAVA 2017. 5. 16. 15:29 |

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); // no cast


제네릭을 사용함으로써 일반적인 알고리즘을 구현하도록 지원한다.

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>("부장");
    }
}



제네릭에 대해 기본적인 개념만 살펴보았다.


'JAVA > JAVA' 카테고리의 다른 글

[객체지향] JAVA SOLID 원칙  (0) 2019.02.23
제네릭 메소드  (0) 2017.05.17
다형성  (0) 2015.08.28
클라이언트 ip 구하기  (0) 2015.08.13
java foreach 반복문  (0) 2015.08.13
Posted by 양승아
: