[Java] 제네릭(Generic)에 대해서
제네릭이란?
제네릭은
클래스 내부에서 사용할 데이터 타입을 클래스 외부에서 지정하는 기법
이다.
상속(extends)와 인터페이스(interface)에서도 사용 가능하다.
제네릭은 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에컴파일 시 타입 체크
를 해주는 기능이다.
제네릭 타입중에 T로 되어있는 경우가 있는데, 하지만 T라는 데이터 타입은 존재하지 않는다.
이 값은 클래스를 선언하는 부분에서 정해진다.
즉, 인스턴스 생성할때 제네릭안에 선언되는 데이터 타입에 의해서 결정된다.
public class GenericClass<T> {
public T info;
}
public class Main {
public static void main(String[] args) {
GenericClass<String> generic = new GenericClass<String>;
GenericClass<Integer> generic = new GenericClass<Integer>;
}
}
(중요) 클래스를 정의할때 해당 클래스 내부에서 사용하는 데이터 타입을 확정하지 않고 인스턴스를 생성할때 데이터 타입을 지정하는것이 제네릭이다.
제네릭을 사용해야하는 경우
제네릭을 사용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있기 때문이다.
타입 안정성
public class StudentInfo {
public int grade;
public StudentInfo(int grade) {
this.grade = grade;
}
}
public class EmployeeInfo {
public int rank;
public EmployeeInfo(int rank) {
this.rank = rank;
}
}
public class Person {
public Object info;
public Person(Object info) {
this.info = info;
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person("반장");
Employee e = new Employee();
}
}
위의 코드에 대해 설명하자면 Person의 생성자의 데이터 타입은 Object이다. Object는 모든 객체가 될 수 있다.
EmployeeInfo의 객체가 아니라 String 타입이 와도 컴파일 에러가 안난다. (대신 런타임 에러가 난다.)
컴파일언어의 기본은 모든 에러는 컴파일이 발생할 수 있도록 유도해야 한다.
하지만 런타임은 실제로 애플리케이션이 동작하고 있는 상황이기 때문에 런타임에 발생하는 에러는 항상 심각한 문제를 초래할 수 있기 때문이다.
그래서 위와 같은 코드에 대해서는 타입에 대해 안전하지 않다고 한다.
즉, 모든 타입이 올 수 있기 때문에 타입을 엄격하게 제한 할 수 없게 되는 것이다.
제네릭화
위의 코드를 제네릭화로 변경해본다.
package generic;
public class GenericClass3 {
public static class StudentInfo {
public int grade;
public StudentInfo(int grade) {
this.grade = grade;
}
}
public static class EmployeeInfo {
public int rank;
public EmployeeInfo(int rank) {
this.rank = rank;
}
}
public static class Person<T> {
public T info;
public Person(T info) {
this.info = info;
}
}
public static void main(String[] args) {
Person<EmployeeInfo> p = new Person<EmployeeInfo>(new EmployeeInfo(1));
EmployeeInfo e = p.info ;
System.out.println(e.rank);
}
}
알고가야할 부분
컴파일 단계에서 오류가 검출
된다.중복의 제거
와타입 안전성을 동시에 추구
할 수 있게 된다.
여러개의 제네릭을 필요로 할때
클래스 내에서 여러개의 제네릭을 필요로 할때가 있다. 그럴때 사용하는 방법을 알아본다.
public class GenericClass4 {
public static class EmployeeInfo {
public int rank;
public EmployeeInfo(int rank) {
this.rank = rank;
}
}
public static class Person<T, X> {
public T info;
public X id;
public Person(T info, X id) {
this.info = info;
this.id = id;
}
}
public static void main(String[] args) {
Integer i = 10;
EmployeeInfo e = new EmployeeInfo(1);
Person<EmployeeInfo, Integer> person = new Person<EmployeeInfo, Integer>(e, i);
System.out.println(person.id.intValue());
}
}
여러개의 제네릭을 사용할땐 <>안에 ,(콤마)
옆에 바로 써주면 된다.
여기서 사용되는 문자는 원하는거 아무거나 써도 되지만 개발자들끼리의 묵시적인 약속이 있다.
(되도록이면 거기에 맞추는게 좋다.)
타입 | 설명 |
---|---|
T | Type |
E | Element |
K | Key |
V | Value |
N | Nnmber |
주의해야 할 점이 하나 있는데 제네릭을 사용할때 기본타입 데이터(primitive type)을 사용할 수 없다.
참조 데이터(클래스, 인터페이스,배열)만 사용가능
한걸 알고 있자.
기본 자료형을 사용하기 위해선 래퍼클래스를 사용해야함
래퍼클래스의 예) String, Integer, ...
제네릭 메서드 적용 방법
public class EmployeeInfo {
public int rank;
public EmployeeInfo(int rank) {
this.rank = rank;
}
}
public class Person<T, X> {
public T info;
public S id;
public Person(T info, X id) {
this.info = info;
this.id = id;
}
public <U> void printInfo(U info) {
// 메서드가 반환형이 없고 타입만 주고 싶을때 <U> 이런식으로 준다.
System.out.println(info);
}
public T printInfo(T info) {
// 메서드의 리턴값이 있을땐 클래스를 선언할때 사용한 제네릭 타입을 사용해야한다.
return info;
}
}
public class GenericClass {
public static void main(String[] args) {
EmployeeInfo e = new EmployeeInfo(1);
Integer i = 10;
Person<EmployeeInfo, Integer> person = new Person<EmployeeInfo, Integer>(e, i);
person.printInfo(e);
}
}
위의 코드를 보면 메서드에 제네릭을 선언한게 보인다 하나의 클래스에 동일하게 제네릭 타입을 두면 더 좋지만 본인이 원하는 대로 타입을 둘 수 있다는 걸 보여주기 위해 코드로 작성해보았다. (반환형이 없는 메서드에 한해서 가능한일)
Author And Source
이 문제에 관하여([Java] 제네릭(Generic)에 대해서), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@conficker77/Java-제네릭Generic에-대해서저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)