ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java - 제네릭(Generic)을 사용하는 이유
    Language/Java 2022. 12. 27. 17:26

    제네릭(Generic)을 사용하는 이유

    제네릭을 사용하지 않을 경우의 문제점 - 타입 안전성
    class StudentInfo {
        public int grade;
        public StudentInfo(int grade) { this.grade = grade; }
    }
    
    class StudentPerson {
        public StudentInfo studentInfo;
        public StudentPerson(StudentInfo studentInfo) { this.studentInfo = studentInfo; }
    }
    
    class EmployeeInfo {
        public int rank;
        public EmployeeInfo(int rank) { this.rank = rank; }
    }
    
    class EmployeePerson {
        public EmployeeInfo employeeInfo;
        public EmployeePerson(EmployeeInfo employeeInfo) { this.employeeInfo = employeeInfo; }
    }
    
    public class GenericInfo {
        public static void main(String[] args) {
            StudentInfo si = new StudentInfo(2);
            StudentPerson sp = new StudentPerson(si);
            System.out.println(sp.studentInfo.grade);   // 2 출력
    
            EmployeeInfo ei = new EmployeeInfo(1);
            EmployeePerson ep = new EmployeePerson(ei);
            System.out.println(ep.employeeInfo.rank);   // 1 출력
        }
    }
    

    위 코드를 살펴보겠다. 위 코드의 StudentPerson과 EmployeePerson 클래스는 똑같은 메커니즘을 가진 코드이다. 이 말은 즉, 두 클래스를 대표할 수 있는 클래스가 존재할 수 있다는 의미이다. 두 클래스에 대한 공통적인 클래스를 만들게 되면, 중복을 제거할 수 있다.

     

    class StudentInfo {
        public int grade;
        public StudentInfo(int grade) { this.grade = grade; }
    }
    
    class EmployeeInfo {
        public int rank;
        public EmployeeInfo(int rank) { this.rank = rank; }
    }
    
    class Person {
        public Object info;
        public Person(Object info) { this.info = info; }
    }

    모든 클래스의 공통 조상인 Object로 데이터 타입을 선언함으로써, 중복을 제거하였다.

     

    public class GenericDemo {
        public static void main(String[] args) {
            Person p = new Person("부장");
            EmployeeInfo ei = (EmployeeInfo)p.info;
            System.out.println(ei.rank);
        }
    }

    중복을 제거한 코드를 실행하기 위해 위와 같은 코드를 작성하였다.

     

    Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to org.opentutorials.javatutorials.generic.EmployeeInfo
        at org.opentutorials.javatutorials.generic.GenericDemo.main(GenericDemo.java:17)

    위 코드는 성공적으로 컴파일되지만, 실행을 하면 위와 같은 오류가 발생한다.

     

    Person p = new Person("부장");

    위 코드를 살펴보겠다. Person 클래스의 생성자의 매개 변수인 info의 데이터 타입은 Object이다. 따라서, 모든 데이터 타입이 매개 변수로 올 수 있다. 그렇기 때문에 EmployeeInfo의 객체가 아닌 String 객체가 와도 컴파일 에러가 발생하지 않는 것이다.

    이러한 상황은 문법적으로 아무런 문제가 없지만, 코드의 취지와 부합하지 않는 방식이다. 이러한 버그들은 컴파일할 때뿐만 아니라 실제로 동작하는 과정에서도 검출되지 않기 때문에 심각한 문제를 야기할 수 있다.

    위와 같은 에러를 타입에 대해서 안전하지 않다고 한다. 즉, 모든 타입이 올 수 있기 때문에 타입을 엄격하게 제한할 수 없게 되는 것이다.

     

    제네릭의 사용
    class StudentInfo {
        public int grade;
        public StudentInfo(int grade) { this.grade = grade; }
    }
    
    class EmployeeInfo {
        public int rank;
        public EmployeeInfo(int rank) { this.rank = rank; }
    }
    
    class Person<T> {
        public T info;
        public Person(T info) { this.info = info; }
    }
    
    public class GenericInfo {
        public static void main(String[] args) {
            Person<EmployeeInfo> p01 = new Person<EmployeeInfo>(new EmployeeInfo(1));
            EmployeeInfo ei01 = p01.info;
            System.out.println(ei01.rank);   // 1 출력
            
            Person<String> p02 = new Person<>("부장");
            String ei02 = p02.info;
            System.out.println(ei02.rank);   // 컴파일 실패
        }
    }

    위 코드는 앞서 Object 타입으로 선언했던 코드를 제네릭을 사용하도록 변경한 것이다. 

    코드를 살펴보면 알 수 있듯이, p02 부분에 컴파일 오류가 발생한다. p02.info가 String이고, String은 rank 필드가 없는데 해당 필드를 호출하고 있기 때문이다. 제네릭을 사용함으로써, 컴파일 단계에서 오류를 검출할 수 있고, 중복의 제거와 함께 타입의 안전성을 향상할 수 있다는 것을 알 수 있다.


    출처

    https://opentutorials.org/module/516/6237

     

    728x90

    댓글

Designed by Tistory.