▶ 제네릭(Generic)
여러 데이터 타입을 하나의 타입으로 일반화시킨다는 의미이다..
컬렉션과 함께 가장 많이 사용되며 저장할 객체를 제한하는 기능으로 한 가지 종류의 클래스만 저장할 수 있게 해 놓은 기능이다.
별도의 제네릭 제시 없이 컬렉션 객체를 생성하게 되면 해당 컬렉션에 다양한 타입의 데이터 값들이 담길 수 있다.
※ 별도의 제네릭 설정을 <Music>으로 하면 해당 컬렉션 안에는 오직 Music객체만 담길 수 있다.
<E>, <T>, <K>, <?>
- 타입변수라고 부르며 내부에 들어간 알파벳 단어에는 기능이 없다. (단, 개발자 간의 암묵적인 규칙이 존재한다.)
- 임의의 정해지지 않은 참조 자료형 타입을 의미한다. 실제 데이터 타입은 컴파일 시점에 정해진다.
※ 컴파일 시점이란 제네릭 클래스 객체 생성, 제네릭이 붙은 매개변수, 반환형이 있는 메서드가 호출될 때를 말한다.
■ 제네릭을 설정하는 이유
1. 명시한 타입의 객체만 저장가능하도록 타입의 제한을 두기 위해서 => 안전성을 확보 가능
2. 컬렉션에 저장된 객체를 꺼내서 사용할 때 매번 형변환하는 절차를 없애기 위해서
■ 제네릭 등장 이전
List l = new ArrayList(); // 컬렉션 안에 담긴 데이터의 자료형은 모른다.
Iterator i = l.iterator();
while (i.hasNext()) {
System.out.println(i.next()); // 꺼내쓸때 어떤 자료형인지 모른다.
}
만약 특정 클래스 안에 담긴 함수를 호출하기 위해 다운캐스팅 하려고 했을 때 내부에 들어가 있는 데이터 타입이 다운캐스팅 하고자 하는 클래스와 상속관계가 아닐 수도 있다. => 형변환 에러가 발생할 수 있다.
따라서 내부적으로 들어갈 데이터 타입을 한정 지을 수 있는 방법을 고민하다가 Generic이 등장했다.
■ main 메서드를 포함한 GenericRun<G, Generic> 클래스 생성
public class GenericRun <G, Generic> {
G g;
Generic gen;
public static void main(String[] args) {
}
}
g와 gen은 클래스나 메서드에 제네릭타입을 사용하게 되면 해당 클래스나 내부에 들어가는 데이터 타입을 컴파일하는 시점에 지정이 된다.
■ 객체 선언
GenericRun<Integer, String> gr1 = new GenericRun();
// 컴파일 되는 시점에 G == Integer, gen == String타입으로 변환되었다.
gr1.g = 10;
gr1.gen = "문자열";
들어갈 데이터 타입을 미리 정의해두지 않음으로써 확장성을 크게 늘리는 게 가능하다.
■ 제네릭의 단점
- 제네릭끼리는 상속관계가 적용되지 않는다.
● 일반배열
Integer[] arr = new Integer[] {1, 2, 3, 4, 5};
Object[] oArr = new Object[5];
oArr = arr; // 다형성의 원리에 의해 UpCasting으로 자동형변환된다.
System.out.println(Arrays.toString(oArr));
<결과>
[1, 2, 3, 4, 5]
● 컬렉션이 있는 ArrayList
ArrayList<Integer> list = new ArrayList();
list.add(1);
list.add(1);
list.add(1);
list.add(1);
ArrayList<Object> list2 = new ArrayList();
list2 = list; // 안된다.
이때 list2 = list; 코드는 에러가 발생한다.
ArrayList<Integer>와 ArrayList<Object>는 완전히 다른 별개의 클래스이기 때문이다.
즉, 제네릭은 오직 <g>로 전달받은 데이터 타입과 일치해야만 서로 받아줄 수 있다.
문제점이 제네릭의 최대 장점이 바로 들어갈 데이터 타입을 미리 정의해두지 않고 컴파일 시점에 정해줌으로써 확장성을 늘려준다인데 이러면 제네릭을 사용하는 의미가 퇴색되어 버린다.
■ 해결방법
<?>타입(와일드카드) : 타입이 정해지지 않은 타입. 제네릭으로 지정할 타입을 지정하지 않았다 라는 의미, 어떤 값이든 들어갈 수 있다.
ArrayList<?> list3 = new ArrayList(); // 어떤 타입의 제네릭값이든 다 담을 수 있게 된다.
list3 = list2; // 들어오는 데이터 타입을 신경쓰지 않는다.
list3 = list;
// any type(x) / unknown type(o)
● <?>타입 ArrayList값 출력하기
Iterator<?> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
꺼낸 데이터값의 자료형은 정해지지 않았기 때문에 모른다. 단순하게 꺼내기만 한다면 에러가 발생하지 않는다.
내부에 어떤 자료형의 데이터가 있는지 모르지만 최소한 Object클래스의 먼 자손일 것이기 때문에 다형성 원리에 의해 println의 매개변수로 넣어줄 수 있다.
■ <?>의 단점
제네릭 설정이 ? 라면 넣고자 하는 객체가 ?(unknownType)이여야한다.
하지만 타입 자체가 정해지지 않은게 ? 이므로, 들어가는 타입이 제네릭으로 지정한 타입인지 논리적으로 검사자체를 할 수가 없기 때문에 에러를 발생시킨다.
?가 가질 수 있는 클래스 범위는 무한대이다.
따라서 제대로 값이 들어갔는지 검사 자체가 불가능하기 때문에 컴파일 단계에서 에러가 발생한 것이다.
즉, ?는 어떤 타입이든 넣을 수 있는 타입변수가 아니라 자료형이 정해지지 않은 뭔지모르겠는(unknown) 타입변수이다.
※ 단, get을 사용해서 컬렉션 안에 들어가 있는 값을 꺼낼 때는 자료형에 대한 검사가 별도로 필요없으므로 에러가 발생하지 않는다.
문제점은 결국 ?가 가질 수 있는 자료형의 범위가 무한대이기 때문에 발생한 것이다.
■ 해결방법
● 상위클래스인 Parent와 Parent의 하위클래스인 Child1과 Child2 생성
class Parent {
}
class Child1 extends Parent {
}
class Child2 extends Parent {
}
1. 상한 경계 설정 : 와일드 카드(?)가 가질 수 있는 자료형의 "최대값"을 설정하는 방법 == 상위클래스를 제한
[표현법]
클래스명<? extends T>
?가 가질 수 있는 최고 높은 자료형은 T이다. 즉, T와 T의 자손들만 제네릭으로 들어올 수 있다.
※ 값을 꺼내쓸 때 사용하는 것을 권장한다.
List<? extends Parent> upperBoundary = new ArrayList();
for (Parent p : upperBoundary) {
System.out.println(p);
}
<결과>
1
1
1
1
List에 들어가 있는 값이 무엇인지 모른다. 하지만 Parent클래스는 상속받았으므로 다형성 원리에 의해 자동형변환이 가능해지기 때문에 에러가 발생하지 않는다.
2. 하한 경계 설정 : 와일드 카드(?)가 가질 수 있는 자료형의 "최소값"을 설정하는 방법 == 하위클래스를 제한
[표현법]
클래스명<? super T>
?가 가질 수 있는 가장 낮은 자료형은 T이다. 즉, T와 T의 조상클래스만 가능하다.
※ 값을 추가할 때 사용하는 걸 권장한다.
List<? super Parent> lowerBoundary = new ArrayList();
for (Object o : lowerBoundary) {
}
와일드카드에 들어가 있는 타입이 뭐가 됐든 Object의 자손이므로 자동형변환된다.
lowerBoundary.add(new Child1());
lowerBoundary.add(new Parent());
경계선상에 더 밑에 존재하기 때문에 UpCasting이 발생해서 위 코드는 가능하다.
lowerBoundary.add(new Object());
경계선상 더 위에 존재하기 때문에 위 코드는 불가능하다.
'JAVA' 카테고리의 다른 글
JAVA (31) Network (0) | 2023.04.20 |
---|---|
JAVA (30) Thread (0) | 2023.04.19 |
JAVA (28) Collection (0) | 2023.04.15 |
JAVA (27) 보조 스트림 (0) | 2023.04.15 |
JAVA (26) FileCharDao (0) | 2023.04.15 |