2020. 11. 2. 21:47ㆍ자바(Java)
자바(Java)의 제네릭(Generic)에 대한 정리
▶ 제네릭(Genric)
class Person<T>{
public T info;
}
Person<String> p1 = new Person<String>();
Person<StringBuilder> p2 = new Person<StringBuilder>();
- T는 info라는 필드의 데이터타입이다.
- Person이 인스턴스화될때 다이아몬드안에 구체적인 데이터타입(String)을 언급을하면
class Person<String>이 되고 info도 String이라는 데이터 타입을 가지게된다.
- 인스턴스를 생성할때 인스턴스변수와 인스턴스는 똑같은 형식을 가지고있어야한다.
- 제네릭은 클래스타입이 미정인 경우 데이터 타입을 인스턴스화 시킬 때 지정하는 것.
▶ 타입 안전성(Type safety)과 제네릭(Generic)이 필요한 이유
package org.opentutorials.javatutorials.generic;
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
}
}
↓중복을 제거한 코드
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);
- 위의 코드를 보면 중복이 발생한다.
- 모든 클래스들의 공통조상인 object를 사용하면 어떠한 클래스의 인스턴스도 올 수 있다.
- 우리는 Person() 이 괄호안에 StudentInfo와 EmployInfo의 인스턴스가 들어오길 기대하는데
나중에 시간이 많이흐르고 개발자가 코드가 짠 의도를 기억하지 못할때 단지 코드만보고
사람이름넣으면 되구나해서 넣고나면 문법적으로는 오류없이 실행이되지만 의도에 빗나감.
이러한 문법적인 오류는 찾기어려운 문제이기때문에 아주 심각한 오류이다.
- 부장이라는 String타입을 EmployeeInfo라고 형변환을 시도했는데 에러메세지가 뜨지않는다.
이는 컴파일할때 오류가 검출되지않는것인데 실행하면 오류가발생한다.(런타임에러)
=> 이를 타입이 안전하지 않다라고 한다.(자바에서는 허용되지 않는다, Java는 타입에 엄격하다.)
- 우리는 컴파일할 때 오류가 검출되게끔 해야한다.
- 그래서 도입된것이 제네릭이라는 것이다.(타입의 안전성, 코드의 중복성을 제거한 편의성)
▶ 제네릭(Generic)의 복수사용과 기본 데이터 타입의 객체화
package org.opentutorials.javatutorials.generic;
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) {
Integer id = new Integer(1);
Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(new EmployeeInfo(1), id);
System.out.println(p1.id.intValue());
}
}
- 제네릭을 여러개 사용하려면 위와같이 다이아몬드안에 ,(콤마)로 구분해주면된다.
(다른 이름을 가져야함)
- T,S에는 기본데이터타입은 올 수 없다.(참조형만 올 수 있음)
- 기본데이터타입은 자바에서 객체가 아니다. 즉, 제네릭을 사용하려면 기본 데이터타입을
객체인것처럼 만들어야하는데 그러한 클래스를 랩퍼클래스(Wrapper Class)라고한다.
- int형식의 데이터타입이었던 숫자 1이 Wrapper Class인 Integer에 생성자로 들어가서 숫자 1을
의미하는 하나의 객체 인스턴스를 만들었다. -> 더 이상 id는 기본 데이터타입이아님.
- Wrapper Class에 있는 메소드중 intValue();를 호출하면 Wrapper Class가 담고있는 원래의 숫자
즉, Int 타입의 데이터타입으로 돌려주는 메소드이다.(원래값가져오기)
▶ 제네릭(Generic)의 생략과 메소드(Method)에 적용하기
package org.opentutorials.javatutorials.generic;
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);
}
}
- Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);는
Info와 id의 매개변수타입을 지정해준다고 볼 수 있는데 자바는 생성자의 매개변수(e,i)를 통해
e와 i가 각각 EmployInfo와 Integer라는것을 알 수 있다. 이러한 경우에는 제네릭을 생략하여
Person p1 = new Person(e, i); 이런식으로써도 자바는 알아볼 수 있다.
- 제네릭은 클래스레벨에서만 사용하는것이아닌 메소드레벨에서도 사용할 수 있다.
- 메소드에서 Info라는 매개변수타입을 확정하지않고 싶을 때 <U>를 사용한다.
- p1.<EmployInfo>printInfo(e);를 사용하면 <EmployInfo>라는 데이터타입이 <U>로 들어가서
info의 데이터타입은 EmployInfo가 된다. 이때, 매개변수 info에 어떠한 값이 들어왔느냐에따라
U를 추정할 수 있기때문에 <EmployInfo>는 생략이가능하다. 즉, p1.printInfo(e);로 쓸 수 있다.
▶제네릭(Generic)의 제한
package org.opentutorials.javatutorials.generic;
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>("부장");
}
}
- 제네릭은 클래스타입이 내부적으로 미정인 경우 데이터 타입을 인스턴스화 시킬 때 지정하는 것.
그렇다보니, 제네릭으로 아무거나 들어올 수 있는것이 문제가 됨. -> extends
- EmployInfo의 부모클래스는 추상클래스인 Info이다.
- T extends Info의 뜻은 Info라는 클래스의 자식클래스들만 T에 데이터타입으로 들어올 수 있다.
따라서 Person<String> p2 = new Person<String>("부장"); 에서
String은 Info의 자식이아니므로 컴파일 에러가 발생하게 된다.
- extends를 Class로만 사용해야하는것이아닌 Interface로 사용해도된다. -> implements
- extends는 제네릭에서 사용할 때 '상속한다'가 아닌 '부모가 무엇이다'로 사용된다.
따라서 Interface로 바꾸더라도 <T extends Info>는 유지된다.
- extends와 반대로 super라는 것도있는데 부모를제한하는것이다.
ref)이번 글은 유튜브 생활코딩님의 강의영상을 참고하였습니다.
'자바(Java)' 카테고리의 다른 글
자바(Java) 스택(Stack)과 큐(Queue) 정리 (0) | 2020.11.06 |
---|---|
자바(Java) 클래스(Class), 인스턴스(Instance), 객체(Object) 정리 (0) | 2020.11.05 |