모든 객체를 Object 타입으로 처리한 결과이다.
ArrayList에 다양한 타입의 데이터를 저장할 수 있지만 데이터를 꺼내 올 때마다 명시적으로 타입 캐스팅이 필요하다.
import java.util.*;
public class Main {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("Hello");
list.add(123);
// 데이터를 꺼낼 때 타입 캐스팅이 필요
String str = (String) list.get(0);
Integer num = (Integer) list.get(1);
}
}

=> 이 경고는 컴파일러가 raw 타입(ArrayList)을 사용했기 때문에 발생한다.
그렇다면 다음 예시를 보겠다.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("Hello"); // String 타입 추가
list.add(123); // Integer 타입 추가
String str1 = (String) list.get(0);
String str2 = (String) list.get(1);
System.out.println(str1);
System.out.println(str2);
}
}
결과가 어떻게 될까 ?

오류가 발생한다.
Integer형 배열, String형 배열 등 배열에 포함되는 원소의 타입마다 추가, 삭제, 정렬과 같은 함수를 정의하고 사용하는 것은 비효율적이라고 생각이 들었다.
또한 어떤 새로운 자료구조를 만드려고 할 때 많은 타입을 지원하고 싶다면 각 타입에 대한 클래스 하나하나 각자 만드는 것도 비효율적이다.
제네릭 ?
사전적 의미로는 '일반적인'이다.
일반적인 ? 잘 이해가 되지 않는다.
그래서 찾아보았다.
데이터의 타입을 일반화한다.
클래스나 메서드 정의 시 일반화하여 사용할 데이터 타입을 컴파일할 때 미리 지정.
...
이라고 하는데 쉽게 내가 이해할 수 있게 정리하자면
"데이터 형식에 의존하지 않고 하나의 값이 여러 다른 데이터 타입들을 가질 수 있도록 하는 방법"
이다.
흔히 쓰는 ArrayList, LinkedList 등을 생성할 때
객체<타입> 객체명 = new 객체<타입>(); 과 같이 생성하는 것처럼
ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<String> list2 = new ArrayList<Integer>();
LinkedList<Double> list3 = new LinkedList<Double>():
LinkedList<Character> list4 = new LinkedList<Character>();
<> 괄호 안에 들어가는 타입을 지정해준다.
제네릭은 클래스 내부에서 지정하는 것이 아니라 외부에서 사용자에 의해 지정된다.
특정 타입을 미리 지정해주는 것이 아닌 필요해 의해 지정할 수 있도록 하는 일반 타입이다.
public class 클래스명<T> { ... }
public interface 인터페이스명<T> { ... }

// T는 제네릭 타입 변수
class Box<T> {
private T item;
// 값을 저장
public void setItem(T item) {
this.item = item;
}
// 값을 반환
public T getItem() {
return item;
}
}
public class Main {
public static void main(String[] args) {
// Box 클래스의 인스턴스를 String 타입으로 생성
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello, Generics!");
String str = stringBox.getItem();
System.out.println("String Box: " + str);
// Box 클래스의 인스턴스를 Integer 타입으로 생성
Box<Integer> intBox = new Box<>();
intBox.setItem(123);
Integer num = intBox.getItem();
System.out.println("Integer Box: " + num);
}
}
// 출력 결과
// String Box: Hello, Generics!
// Integer Box: 123
public class Main {
// 제네릭 메서드: T 타입의 파라미터와 반환값을 사용
public static <T> void printItem(T item) {
System.out.println("Item: " + item);
}
public static void main(String[] args) {
// 제네릭 메서드 호출
printItem("Hello, Generics!"); // String 타입
printItem(123); // Integer 타입
printItem(45.67); // Double 타입
}
}
// 출력 결과
// Item: Hello, Generics!
// Item: 123
// Item: 45.67
List<String> list = new ArrayList<>();
list.add("Hello");
list.add(123); // 컴파일 오류: 타입이 맞지 않음
List<String> list = new ArrayList<>();
list.add("Hello");
String str = list.get(0); // 타입 캐스팅 불필요
class Box<T> {
private T item;
public void setItem(T item) { this.item = item; }
public T getItem() { return item; }
}
List<Integer> intList = new ArrayList<>(); // Integer 타입 리스트임을 알 수 있음
제네릭은 배열 생성이 불가하다 !
왜 ? 타입 소거로 인해 런타임에 타입 정보를 보장할 수 없기 때문에
T[] array = new T[10]; // 컴파일 오류
개발자들은 귀찮은것을 싫어하는거같다...ㅋ