[Day13] Generic 심화
[Day13] Generic 심화
typescript 수업 배울 때도 어려웠던 generic에 대해서 오늘은 java로 학습했다.
Generic 심화
- generic은 재사용성, 타입 안정성, 유지보수성을 높여준다.
성능향상을 위한 제네릭
➡ 제네릭을 사용하면 타입이 일관되도록 강제할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package generic;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<A> list=new ArrayList<>(); // A타입의 객체만 저장
list.add(new A());
list.add(new B()); // B는 A를 상속
// ❌ 컴파일 에러
list.add("java"); // 문자열은 A 타입이 아님
list.add(1); // 정수도 A 타입이 아님
list.add(new String("java")); // A 타입이 아님
list.add(new StringBuilder("java")); // StringBuilder도 A와 관계 없음
}
}
class A{}
class B extends A{}
extends 와일드 카드(<? extends A>) 사용이 필요한 경우
문제점
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Test {
public static void main(String[] args) {
List<B> listB=new ArrayList<>();
listB.add(new B());
Biz.service(listB); // 에러
}
}
class A{}
class B extends A{}
class Biz{
public static void service(List<A> list) { // list<B>를 받을 수 없는 메서드
for(A o:list) {
//중요한 일...
}
}
}
해결 방법 : <? extends A> 사용
List<? extends A>
는 A를 포함하여 A의 자식 클래스(B, C, D…)도 받을 수 있음- list에 값을 추가하는 것은 불가능
A, B, C 등 정확한 타입이 보장되지 않기 때문 ```java public class Test { public static void main(String[] args) { List listB=new ArrayList<>(); listB.add(new B());
1
Biz.service(listB); } }
class A{} class B extends A{}
class Biz{ public static void service(List<? extends A> list) { for(A o:list) { //중요한 일… } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
> ❓❓❓ list에 값을 추가하는 것은 불가능
> why? `<? extends A>` 는 읽기 전용 -> 정확히 어떤 하위 타입인지 모르기 때문
>
> ```java
> List<? extends A> list = listB; // ✅ B의 리스트를 받을 수 있음
> // list.add(new A()); // ERROR
> // list.add(new B()); // ERROR
>
> A item = list.get(0); // 가능 (리스트에서 A 타입으로 값을 읽어올 수 있음)
> System.out.println(item); //generic.Test$B@1b6d3586
> ```
## super 와일드카드 (<? super B>) 사용이 필요한 경우
### 문제점
```java
public class Test {
public static void main(String[] args) {
List<A> list=new ArrayList<>();
list.add(new A());
list.add(new B());
Biz.service(list);//컴파일 에러
}
}
class A{}
class B extends A{}
class Biz{
public static void service(List<B> list) { // list<A>를 받을 수 없는 메서드
for(A o:list) { ... }
}
}
해결 방법 (<? super B> 사용)
List<? super B>
는 B와 B의 부모(A)를 포함한 리스트를 받을 수 있음- 가져올 때는 Object로 가져와야 함
- 왜냐하면 정확히 어떤 타입인지 보장이 안됨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test {
public static void main(String[] args) {
List<A> list=new ArrayList<>();
list.add(new A());
list.add(new B());
Biz.service(list);
}
}
class A{}
class B extends A{}
class Biz{
public static void service(List<? super B> list) {
for(Object o:list) { ... } // A만 들어있다고 보장할 수 없기 때문에 가장 상위 타입인 Object로 받는다.
}
}
➡ 따라서 Object 타입으로 받아야 더 안전함.
Method Generic
- 메서드 내부에서 타입을 지정할 수 있도록 하는 기능
💡 제네릭 메서드 선언:를 사용하여 메서드 내부에서 타입을 결정
1
2
3
4
5
6
7
8
9
10
11
public class GenericMethodExample {
public static <T> void printItem(T item) {
System.out.println("Item: " + item);
}
public static void main(String[] args) {
printItem("Hello"); // String 타입
printItem(100); // Integer 타입
printItem(3.14); // Double 타입
}
}
문제점 (수업 코드)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
public static void main(String[] args) {
List<A> list=new ArrayList<>();
list.add(new A());
list.add(new B());
Biz.service(list);
Biz.service(1);
}
}
class A{}
class B extends A{}
class Biz{
public static <T> void service(T list) {
for(A o:list) { // ❌ 컴파일 에러
//중요한 일...
}
}
}
- 이렇게 작성하면 list가 어떤 타입이든 받을 수 있음
- 하지만 for-each 문을 사용하려면 list가 반드시 Iterable
여야 함. - (ex) List
, Set , Queue
- (ex) List
- list가 Integer 같은 반복 불가능한 타입이라면? → 컴파일 오류 발생
해결 방법 (정제된 Method Generic)
public static <T> void service(List<T> list) { ... }
- List
타입만 받을 수 있도록 제한됨 - T가 정확한 타입을 유지할 수 있음
END
This post is licensed under CC BY 4.0 by the author.