Generics
제네릭이란?
List<String>에서 <> 안의 String.
자료형(타입)을 코드가 실행되기 전에 미리 고정하지 않고 외부에서 나중에 넣게끔 설계하는 문법이다.
쉽게 말하면 자료형을 직접 넣지 말고 <>로 비워두고 나중에 이건 String이야~, 이건 Integer야~ 라고 지정하게 하자.
왜 제네릭을 쓰는가?
타입마다 클래스를 일일이 만들면 중복이 심하고 유지보수도 어려움.
제네릭은 하나의 클래스만 정의해두고 모든 타입에 재사용 가능하게 해준다.
→ 아래 제네릭 전/후 예시 참고.
public class StringBox {
private String item;
public void set(String item) {
this.item = item;
}
public String get() {
return item;
}
}
위 코드와 같은 클래스가 있다고 가정해보자. 이거는 String 만 담을 수 있는 박스. 그럼 int도 담고 싶을 때 또 만들어야 하나?라는 생각을 할 수 있다.
public class IntegerBox {
private Integer item;
public void set(Integer item) { this.item = item; }
public Integer get() { return item; }
}
public class DoubleBox {
private Double item;
public void set(Double item) { this.item = item; }
public Double get() { return item; }
}
public class BooleanBox {
private Boolean item;
public void set(Boolean item) { this.item = item; }
public Boolean get() { return item; }
}
이건 타입마다 클래스를 일일이 새로 만들어야 한다. 중복이 너무 심함.
그래서 이런 중복을 없애고 하나의 클래스로 모든 타입을 처리할 때 제네릭.
public class Box<T> {
private T item;
public void set(T item) { this.item = item; }
public T get() { return item; }
}
따라서 제네릭은 재사용 가능한 코드를 만들기 위해 필수
또한 List는 내부 요소의 타입을 모르는 상태로 만들어진다. 그래서 타입 매개변수를 필요로 함.
그래서 List<T> 이런 식으로 선언되어 있고 사용 시 List<String>처럼 구체적인 타입을 만들어줘야 한다.
즉, List, Map, Set같은 컬렉션은 내부적으로 제네릭으로설계되어 있다.
생성된 제네릭은 아래 코드와 같이 사용하면 된다.
Box<String> strBox = new Box<>(); // StringBox 역할
Box<Integer> intBox = new Box<>(); // IntegerBox 역할
Box<Double> doubleBox = new Box<>(); // DoubleBox 역할
Box<Boolean> boolBox = new Box<>(); // BooleanBox 역할
제네릭의 기초 문법
class Box<T> {
private T item;
public void set(T item) { this.item = item; }
public T get() { return item; }
}
T는 타입 매개변수(Type parameter)
클래스나 메서드가 어떤 타입을 받을지 미정인 상태로 설계 가능하다.
Box<String> strBox = new Box<>();
strBox.set("Hello");
String val = strBox.get(); // 형변환 없이 사용 가능!
제네릭 클래스 vs 제네릭 메서드
제네릭 클래스
class Container<T> {
T data;
}
제네릭 메서드
public class Util {
public static <T> void print(T value) {
System.out.println(value);
}
}
<T>가 리턴 타입 앞에 있다. 메서드 자체가 제네릭이라는 뜻이ㅏㄷ.
제네릭 사용 시 주의 사항
class MyClass<T> { ... } // 제네릭 클래스
<T> void myMethod(T val) // 제네릭 메서드
List<int> list = new ArrayList<>(); // 오류 (원시 타입 불가)
Box<String> b = new Box<>();
b.set("hi"); // ok
b.set(123); // 오류 (String만 허용)
질문사항
Q1. <> 안엔 꼭 T여야 해?
=> No. 관례적으로 T를 많이 씀
Q2. <?>는 뭐야?
와일드카드. “모르겠지만 뭔가 타입이 있을 거야” 라는 뜻.
제한을 줄 수 있음 → <? extends Number>, <? super Integer>
Q3. 제네릭은 형변환 안 해도 된다고?
=>Yes, 제네릭은 컴파일 타임에 타입을 검사해줌 → 실수 예방, 가독성 향상
List names = new ArrayList(); // 제네릭 미사용
names.add("hi");
String name = (String) names.get(0); // 형변환 필요 x 위험
List<String> names = new ArrayList<>(); // 제네릭 사용
String name = names.get(0); // 형변환 필요 없음 안전
Q4. 제네릭 클래스 vs 제네릭 메서드 구분 맞아?
=> Yes,
제네릭 클래스
public class Box<T> {
T item;
}
→ T는 클래스 전체에 적용
제네릭 메서드
public class Util {
public static <T> void print(T value) {
System.out.println(value);
}
}
→ <T>가 메서드 안에서만 사용됨. 클래스 전체는 제네릭 아님.
'Spring_부캠 > 두고두고 볼 개념' 카테고리의 다른 글
[Spring] HTTP & 네트워크 & Web 기초 입문 (1주차 강의 정리) (1) | 2025.07.22 |
---|---|
[Java] Lambda & Streams (1) | 2025.07.18 |
[Java] enum (2) | 2025.07.17 |
[Java] Optinal 대체 왜 쓰는데? (2) | 2025.07.11 |
[Java] Quize (1) | 2025.07.11 |