149 lines
5.0 KiB
Markdown
149 lines
5.0 KiB
Markdown
# **자바 제너릭(Generic) 쉽게 배우기**
|
|
|
|
## **1. 제너릭이 뭐야?**
|
|
제너릭(Generic)은 **클래스나 메서드에서 사용할 데이터 타입을 미리 지정하지 않고, 나중에 정할 수 있게 해주는 기능**이다.
|
|
쉽게 말해 **"박스를 만들되, 안에 뭐가 들어갈지는 나중에 정하는 것"**과 같다.
|
|
|
|
예를 들어, `List`는 여러 종류의 데이터를 저장할 수 있다.
|
|
```java
|
|
List<String> strList = new ArrayList<>();
|
|
List<Integer> intList = new ArrayList<>();
|
|
```
|
|
여기서 `List<String>`은 문자열만, `List<Integer>`는 정수만 저장할 수 있다.
|
|
이처럼 **제너릭을 사용하면 타입을 강제할 수 있어 코드의 안정성이 높아진다.**
|
|
|
|
---
|
|
|
|
## **2. 제너릭을 사용하지 않으면? (비교 예제)**
|
|
제너릭을 사용하지 않는 코드:
|
|
```java
|
|
List list = new ArrayList();
|
|
list.add("Hello");
|
|
list.add(123); // 문자열 리스트인데 숫자가 들어감
|
|
|
|
String str = (String) list.get(1); // 실행 시 오류 발생 가능!
|
|
```
|
|
위 코드에서는 `List`가 **어떤 타입의 데이터를 가질지 정해져 있지 않다.**
|
|
따라서 다른 타입의 데이터가 들어와도 **컴파일러가 오류를 잡아주지 못한다.**
|
|
|
|
---
|
|
|
|
## **3. 제너릭을 사용한 코드 (안전한 코드!)**
|
|
```java
|
|
List<String> list = new ArrayList<>();
|
|
list.add("Hello");
|
|
// list.add(123); // 컴파일 오류 발생! (안전성 증가)
|
|
|
|
String str = list.get(0); // 타입 캐스팅 없이 사용 가능
|
|
```
|
|
✅ **제너릭을 사용하면 타입을 제한할 수 있어 실수를 줄이고, 형 변환 없이 안전하게 데이터를 사용할 수 있다!**
|
|
|
|
---
|
|
|
|
## **4. 제너릭 클래스 만들기**
|
|
제너릭을 직접 만들어 보자!
|
|
```java
|
|
class Box<T> { // T는 타입 매개변수
|
|
private T item;
|
|
|
|
public void setItem(T item) {
|
|
this.item = item;
|
|
}
|
|
|
|
public T getItem() {
|
|
return item;
|
|
}
|
|
}
|
|
```
|
|
이제 `Box<T>`를 다양한 타입으로 사용할 수 있다.
|
|
```java
|
|
Box<String> strBox = new Box<>();
|
|
strBox.setItem("Hello");
|
|
System.out.println(strBox.getItem()); // Hello
|
|
|
|
Box<Integer> intBox = new Box<>();
|
|
intBox.setItem(100);
|
|
System.out.println(intBox.getItem()); // 100
|
|
```
|
|
✅ **제너릭을 사용하면 같은 코드로 여러 타입을 지원할 수 있어 코드의 재사용성이 높아진다!**
|
|
|
|
---
|
|
|
|
## **5. 제너릭 메서드 만들기**
|
|
제너릭은 클래스뿐만 아니라 **메서드에서도 사용할 수 있다.**
|
|
```java
|
|
class Util {
|
|
public static <T> void printItem(T item) {
|
|
System.out.println(item);
|
|
}
|
|
}
|
|
|
|
Util.printItem("Hello"); // Hello
|
|
Util.printItem(123); // 123
|
|
Util.printItem(3.14); // 3.14
|
|
```
|
|
✅ **제너릭 메서드를 사용하면 여러 타입을 지원하는 함수를 쉽게 만들 수 있다!**
|
|
|
|
---
|
|
|
|
## **6. 제너릭 타입 제한하기 (extends 사용)**
|
|
어떤 타입이든 받을 수 있는 것도 좋지만, **특정 타입만 허용하고 싶을 때**가 있다.
|
|
이럴 때 `extends` 키워드를 사용해 제한할 수 있다.
|
|
```java
|
|
class NumberBox<T extends Number> { // Number 또는 그 자식 타입만 가능
|
|
private T num;
|
|
|
|
public NumberBox(T num) {
|
|
this.num = num;
|
|
}
|
|
|
|
public double getDoubleValue() {
|
|
return num.doubleValue();
|
|
}
|
|
}
|
|
```
|
|
사용 예시:
|
|
```java
|
|
NumberBox<Integer> intBox = new NumberBox<>(10);
|
|
System.out.println(intBox.getDoubleValue()); // 10.0
|
|
|
|
NumberBox<Double> doubleBox = new NumberBox<>(5.5);
|
|
System.out.println(doubleBox.getDoubleValue()); // 5.5
|
|
|
|
// NumberBox<String> strBox = new NumberBox<>("Hello"); // 오류 발생!
|
|
```
|
|
✅ **제너릭에 `extends`를 사용하면 특정 타입만 허용할 수 있어 더 안전한 코드 작성이 가능하다!**
|
|
|
|
---
|
|
|
|
## **7. 와일드카드 (`?`) 활용하기**
|
|
제너릭을 사용하다 보면 **다양한 타입을 받아야 하지만, 타입을 정확히 모를 때**가 있다.
|
|
이럴 때 **와일드카드 (`?`)** 를 사용할 수 있다.
|
|
|
|
```java
|
|
public static void printList(List<?> list) {
|
|
for (Object obj : list) {
|
|
System.out.println(obj);
|
|
}
|
|
}
|
|
```
|
|
사용 예시:
|
|
```java
|
|
List<String> strList = Arrays.asList("A", "B", "C");
|
|
List<Integer> intList = Arrays.asList(1, 2, 3);
|
|
|
|
printList(strList); // A, B, C
|
|
printList(intList); // 1, 2, 3
|
|
```
|
|
✅ **`?`를 사용하면 어떤 타입이든 받을 수 있어 유연한 코드 작성이 가능하다!**
|
|
|
|
---
|
|
|
|
## **8. 정리**
|
|
✅ **제너릭을 사용하면 코드의 타입 안정성이 증가하고, 불필요한 형 변환을 줄일 수 있다!**
|
|
✅ **제너릭 클래스(`Box<T>`)를 사용하면 여러 타입을 지원하는 재사용 가능한 클래스를 만들 수 있다!**
|
|
✅ **제너릭 메서드(`printItem<T>(T item)`)를 사용하면 다양한 타입을 처리하는 메서드를 쉽게 작성할 수 있다!**
|
|
✅ **`extends`를 사용하면 특정 타입만 허용할 수 있다!**
|
|
✅ **`?`(와일드카드)를 사용하면 유연한 코드 작성이 가능하다!**
|
|
|
|
자바 제너릭을 활용하면 **더 안전하고 재사용 가능한 코드**를 작성할 수 있다! |