개발놀이터

제네릭 본문

Java

제네릭

마늘냄새폴폴 2022. 7. 17. 21:42

제네릭

자바에서 제네릭이란 데이터의 타입을 일반화한다는 것을 의미한다. 제네릭은 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법이다. 이렇게 컴파일 시에 미리 타입 검사를 수행하면 다음과 같은 장점을 지닌다.

 

  1. 클래스나 메소드 내부에서 사용되는 객체의 타입 안전성을 높일 수 있다.
  2. 반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있다. 

 

자바5 이전에는 여러 타입을 사용하는 대부분의 클래스나 메소드에서 인수나 반환값으로 Object타입을 사용했다. 하지만 이 경우에는 반환된 Object객체를 다시 원하는 타입으로 타입 변환해야 하며, 이때 오류가 발생할 가능성도 존재한다. 하지만 자바5부터 도입된 제네릭을 사용하면 컴파일 시에 미리 타입이 정해지므로, 타입 검사나 타입 변환과 같은 번거로운 작업을 생략할 수 있게 된다.

 

그래 다 좋다 타입 안전성때매 제네릭을 사용하는것도 알겠고 평소에도 List객체로 많이 사용하는것도 알겠다. 근데 문제는 바로 제네릭 클래스, 제네릭 메서드다 얘네는 왜 사용할까?

 

제네릭을 사용하는 이유

 

class StudentInfo {
	
    public int grade;
    StudentInfo(int grad) { 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과 EmployeeInfo가 사실상 같은 구조를 가지고 있다. 중복이 발생하고 있는 것이다. 중복을 제거해보자

 

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("부장");
        EmployeeInfo ei = (EmployeeInfo) p1.info;
        System.out.println(ei.rank);
    }
}

위의 코드는 성공적으로 컴파일된다. 하지만 실행을 하면 오류가 발생한다. 어디서 오류가 발생하는걸까?

 

바로 이부분이다.

 

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

 

클래스 Person의 생성자는 매개변수 info의 데이터 타입이 Object이다. 따라서 모든 객체가 될 수 있다. 그렇기 때문에 위처럼 EmployeeInfo의 객체가 아니라 String이 와도 컴파일 에러가 발생하지 않는다. 대신 런타임 에러가 발생한다. 컴파일 언어의 기본은 모든 에러는 컴파일에 발생할 수 잇도록 유도해야 한다는 것이다. 런타임은 실제로 애플리케이션이 동작하고 있는 사오항이기 때문에 런타임에 발생하는 에러는 항상 심각한 문제를 초래할 수 잇기 때문이다.

 

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

 

이것을 제네릭으로 바꿔보자

 

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> {
	
    publit 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);	//컴파일 실패
    }
}

p1은 잘 작동할 것이다. 중요한 것은 p2다. p2는 컴파일 오류가 발생하는데 p2.info가 String이고 String은 rank필드가 없는데 이것을 호출하고 있기 때문이다. 여기서 중요한 것은 아래와 같이 정리할 수 있다. 

  • 컴파일 단계에서 오류가 검출된다.
  • 중복의 제거와 타입 안전성을 동시에 추구할 수 있게 되었다. 

 

출처 : https://edu.goorm.io/learn/lecture/41/%EB%B0%94%EB%A1%9C%EC%8B%A4%EC%8A%B5-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-%EC%9E%90%EB%B0%94-java/lesson/807/%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0

'Java' 카테고리의 다른 글

면접 준비 : 클래스와 객체  (0) 2022.07.28
어노테이션  (0) 2022.07.20
컬렉션 프레임워크 (Collection Framework)  (0) 2022.07.15
GC (Garbage Collection)의 알고리즘  (0) 2022.07.14
JVM (Java Virtual Machine)  (0) 2022.07.14