---
# System prepended metadata

title: 제네릭
tags: [과외(하희영)]

---

# 제네릭

![](https://i.imgur.com/wQvvOD3.png)

**클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법**

- 타입 안정성: 컴파일 시점에 타입 검사를 수행하여 타입 오류를 줄이며 런타임 시점에 ClassCastException과 같은 오류를 방지할 수 있다.
- 코드 중복 감소: 다양한 타입에 대해 동일한 로직을 수행하는 클래스나 메소드를 작성할 때, 제네릭을 사용하면 중복 코드를 최소화할 수 있다. 
    - 다양한 타입의 객체를 저장하는 컬렉션 클래스를 작성할 때 제네릭을 사용하면 하나의 구현으로 여러 타입을 지원할 수 있다.
- 가독성 향상: 제네릭을 사용하면 코드에서 명시적인 타입 변환을 줄일 수 있으며 코드가 간결해지고 가독성이 향상된다.

## 사용법

![](https://i.imgur.com/jcUquyK.png)

**Sample Code**
```java
// 제네릭 클래스 예시
public class GenericBox<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

// 제네릭 메소드 예시
public static <T> void genericPrint(T[] items) {
    for (T item : items) {
        System.out.print(item + " ");
    }
    System.out.println();
}
```

---

## 타입 한정 키워드 `extends`

- 제네릭에서 특정 클래스 또는 인터페이스를 상속하거나 구현한 타입만을 허용하는 제한을 두는 기능
    - 제네릭 클래스나 메서드의 사용 범위를 제한하고, 해당 타입이 가지는 메소드나 속성에 대한 접근을 보장할 수 있게 해준다
- 제네릭에 `extends` 키워드를 붙여줌으로써 제네릭의 타입을 한정지어줄 수 있다.
    - 클래스와 인터페이스 모두를 한정할 수 있다
    - 인터페이스를 한정하려면 & 기호를 사용하여 구분한다
- 기본적인 용법은 `<T extends [제한 타입]>` 이다


``` java
// 한정 타입을 사용한 제네릭 클래스 예시
public class BoundedGenericBox<T extends Number> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

// 한정 타입을 사용한 제네릭 메소드 예시
public static <T extends Comparable<T>> T findMax(T[] items) {
    T maxItem = items[0];
    for (T item : items) {
        if (item.compareTo(maxItem) > 0) {
            maxItem = item;
        }
    }
    return maxItem;
}
```

---

## 제네릭 형변환

- 일반 변수 타입과 달리 **제네릭 서브 타입간에는 형변환이 불가능**하다.
    - 대입된 타입이 `Object`여도 불가능하다
---
``` java
// 배열은 OK 
Object[] arr = new Integer[1];

// 제네릭은 ERROR 
List<Object> list = new ArrayList<Integer>();
```
---
``` java
List<Object> listObj = null; 
List<String> listStr = null; 

// 에러. List<String> -> List<Object>
listObj = (List<Object>) listStr;

// 에러. List<Object> -> List<String>
listStr = (List<String>) listObj;
```
---

- 제네릭 와일드카드를 이용한 형변환은 허용되지 않는다. 이는 자바의 제네릭 타입 시스템이 타입 안전성을 보장하기 위한 제한이다.
- 이런 제네릭 간의 형변환을 성립하기 위해서는 제네릭에서 제공하는 와일드카드 `?`문법을 이용해야 한다.

---
``` java
List<?> list = null;
List<String> listStr = null;

// 올바른 형변환. List<String> -> List<?>
list = listStr;

```

### 제네릭 와일드카드
- 제네릭 타입의 범위를 지정할 때 사용한다
- 와일드카드를 사용하면 다양한 타입의 객체를 한 번에 처리할 수 있으며, 타입 안전성을 유지하면서 유연한 코드를 작성할 수 있다

---
- `<?>` : Unbounded Wildcards (제한 없음)
    - 타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있다
    ``` java
    public static void printItems(List<?> items) {
        for (Object item : items) {
            System.out.println(item);
        }
    }
    ```
---
- `<? extends 상위타입>` : Upper Bounded Wildcards (상위 클래스 제한)
    - 타입 파라미터를 대치하는 구체적인 타입으로 상위 타입이나 상위 타입의 하위 타입만 올 수 있다
    ``` java
    public static double sumOfList(List<? extends Number> numbers) {
        double sum = 0;
        for (Number number : numbers) {
            sum += number.doubleValue();
        }
        return sum;
    }
    ```
---
- `<? super 하위타입>` : Lower Bounded Wildcards (하위 클래스 제한)
    - 타입 파라미터를 대치하는 구체적인 타입으로 하위 타입이나 하위 타입의 상위 타입만 올 수 있다
    ``` java
    public static void addIntegersToList(List<? super Integer> list) {
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
    }
    ``` 
---

## 과제

목표 : 자바로 ArrayList를 구현해본다.

**요구사항**
- 제네릭 클래스를 사용하여 다양한 데이터 타입을 저장할 수 있는 어레이 리스트(`MyArrayList`) 클래스를 구현한다.
- `MyArrayList` 클래스는 다음의 메소드를 포함해야 한다
    - `public void add(T data)`: 어레이 리스트의 끝에 새로운 요소를 추가한다.
    - `public void add(int index, T data)`: 지정된 인덱스에 새로운 요소를 삽입한다.
    - `public T remove(int index)`: 지정된 인덱스의 요소를 삭제하고, 삭제된 요소의 데이터를 반환한다.
    - `public T get(int index)`: 지정된 인덱스의 요소 데이터를 반환한다.
    - `public int size()`: 어레이 리스트의 크기(요소 수)를 반환한다.
    - `public boolean isEmpty()`: 어레이 리스트가 비어 있는지 확인한다.
- 작성한 `MyArrayList` 클래스를 사용하여 다양한 데이터 타입의 요소를 추가, 삭제, 조회할 수 있는 예제도 같이 작성한다.


###### tags: `과외(하희영)`