# 제네릭 ![](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: `과외(하희영)`