✅ 1. 제네릭이란?
public class Box<T> {
private T item;
public void set(T item) {
this.item = item;
}
public T get() {
return item;
}
}
public class Main {
Box<String> box = new Box<>();
box.set("String"); // <String> 제네릭 이기 때문에 타입은 String
}
- 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법을 의미한다.
- 즉, 클래스, 인터페이스, 메소드를 정의할 때 타입 매개변수를 선언하고 자료형의 제한을 둠으로써 타입 안전성을 가진다.
- 제네릭을 사용하면, 자바 프로그램의 재사용성을 높이고 오류를 줄일 수 있게 된다.
- 다양한 형태를 받을 수 있는 클래스를 만들 때 사용을 하며, 형변환을 사용하지 않을 수 있다. ( 보통은 Object인데 사용x )
(1) 참고 사항
- 제네릭을 사용한 클래스를 생성 할 때는 타입 파라미터를 명시해도되고 안해도 된다. 하지만, 하는게 일반적임.
- 타입 파라미터는 기본자료형은 사용이 불가능하며, 참조자료형만 사용이 가능하다.
- 타입 파라미터는 필드의 자료형, 메소드 반환형, 인자의 자료형으로 사용이 가능하다.
- 자료형 매개변수 즉, 타입 매개변수를 가지는 클래스와 인터페이스를 제네릭 타입이라고 한다.
- 타입 파라미터는 하나가 아닌 여러개도 가능하다. <T1, T2 ...>
- 타입 파라미터의 이름은 관례적으로 E, K, V, N, T ... 을 사용 한다.
✅ 2. 제네릭 타입 객체 생성 방법
public class Main {
Data<String> data = new Data<>(); // ✅ 타입 명시 + 다이아몬드 연산자 사용
Data data = new Data(); // ✅ raw type 사용 (비추천)
Data<String> data = new Data<String>(); // ✅ 타입 전체 명시
}
(1) Data<String> data = new Data<>(); - 권장 되는 방식
- 제네릭 타입을 명시하고, 우측은 다이아몬드 연산자<>를 사용해 타입 추론을 맡기는 방식이다.
(2) Data data = new Data(); - 비권장 방식
- 제네릭 타입을 명시하지 않아서 컴파일러가 타입을 추론하지 못한다. 즉, Object로 간주되어서 들어가게 된다.
- 쉽게말해, Data<Object> data = new Data<>(); 형식으로 들어간다는 의미이다.
(3) Data<String> data = new Data<String>();
- 양쪽 모두 타입을 명시한 전통적인 제네릭 사용 방식으로, 코드가 길고 중복이 된다는 단점이 있다.
✅ 3. 제네릭 인터페이스
// 제네릭 인터페이스 정의
interface Printer<T> {
void print(T item);
}
// 제네릭 인터페이스 구현 - String 타입 전용
class StringPrinter implements Printer<String> {
public void print(String item) {
System.out.println("문자열 출력: " + item);
}
}
// 제네릭 인터페이스 구현 - Integer 타입 전용
class IntegerPrinter<T> implements Printer<Integer> {
public void print(Integer item) {
System.out.println("정수 출력: " + item);
}
}
// 메인 클래스
public class Main {
public static void main(String[] args) {
Printer<String> stringPrinter = new StringPrinter();
stringPrinter.print("안녕하세요");
Printer<Integer> intPrinter = new IntegerPrinter<>();
intPrinter.print(123);
}
}
- 제네릭 인터페이스를 만들게되고, 구현 클래스는 인터페이스 클래스 제네릭 타입을 명시해줘야 한다. 명시를 하게 되면, 구현하는 추상메서드의 타입은 명시한 타입으로 정의를 해야 된다. 안그러면 오류가 난다.
✅ 4. 제네릭 메소드
(1) 인스턴스 제네릭 메소드
public class Printer {
public <T> void print(T data) {
System.out.println(data);
}
}
// 타입 추론 메서드 호출
public class Main {
public static void main(String[] args) {
Printer p = new Printer();
p.print("Hello"); // 문자열
p.print(1); // 정수
}
}
// 타입매개변수 명시 메서드 호출
public class Main {
public static void main(String[] args) {
Printer p = new Printer();
p.<String>print("Hello");
p.<String>print(1); // 오류 : 매개변수 값 String 타입이 아님.
}
}
- 매개변수나 반환값의 타입을 메서드 호출 시점에 지정할 수 있도록 만든 메서드를 의미한다.
- 쉽게말해, 호출 당하는 쪽 메서드의 <> 제네릭을 부여함으로써, 매개변수 및 반환값의 타입을 맞춰주는 방식이다.
- 하나의 메소드 정의로 여러 유형의 데이터를 처리할 때 유용하다.
- 메서드를 호출할 때 타입매개변수를 생략해도 매개변수 타입으로 추론해서 넣어준다.
- 하지만, 타입매개변수를 넣을 수도 있는 형태이다.
(1) 클래스 제네릭 메소드 ( statck 제네릭 메소드)
public class Printer {
public static <T> void print(T data) {
System.out.println(data);
}
}
// 타입 추론 메서드 호출
public class Main {
public static void main(String[] args) {
Printer.print("Hello"); // 문자열
Printer.print(1); // 정수
}
}
// 타입매개변수 명시 메서드 호출
public class Main {
public static void main(String[] args) {
Printer.<String>print("Hello");
Printer.<String>print(1); // 오류 : 매개변수 값 String 타입이 아님.
}
}
- 위의 인스턴스 제네릭 메서드와 동일하게 타입매개변수를 넣거나 생략할 수 있다.
- 일반 static 메서드에서 제네릭이 들어간 형태이다.
✅ 5. 제네릭 타입 제한
public class Data<T extends Number> {
public void print(T data) {
System.out.println(data); // T 자료형에 맞는 변수값 출력
}
}
public class Main {
public static void main(String[] args) {
Data<Integer> data = new Data<>();
data.print(123);
Data<String> data = new Data<>(); // 컴파일 에러: String은 Number의 하위 클래스가 아님.
}
}
- 제네릭 클래스, 제네릭 인터페이스, 제네릭 메소드를 정의할 때 적용가능한 자료형에 제한을 두는 것을 의미한다.
- 제네릭 매개변수로 자료형 타입의 제한을 두는 것이며, 서브 클래스인 경우에만 가능하게 제한을 둘 수 있다.
'공부 > ☕Java' 카테고리의 다른 글
| [Java] java.lang 패키지 정리: Object, String, Math 클래스 총정리 (0) | 2025.05.26 |
|---|---|
| [Java] 자바 함수형 인터페이스와 람다식(Lambda Expression) 쉽게 배우기 (1) | 2025.05.23 |
| [Java] 익명 클래스(Anonymous Class) (1) | 2025.05.22 |
| [Java] 다형성(polymorphism) (1) | 2025.05.22 |
| [Java] 추상화 - 추상클래스, 추상메서드, 인터페이스 (0) | 2025.05.20 |