티스토리 뷰
JAVA로 애플리케이션을 만들 때 그냥 아무 생각 없이 <> 안에 객체를 넣어서 해당 타입만 들어오게만 하도록 사용하였습니다.
어떻게 사용되는지 모르고 그냥 썼었는데 이번을 계기로 개념과 제대로 된 사용법을 익힐라고 합니다.
제네릭(Generic) 이란?
다양한 타입의 객체들을 다루는 메서드나 클래스에 컴파일 시 타입체크를 해주는 기능이다.
클래스나 메서드에서 사용할 내부 데이터 타입을 외부에서 지정하는 방법이다.
/** 제네릭 미사용 **/
List stringList = new ArrayList<>();
stringList.add("코딩맨"); // 추가 된다.
stringList.add(1); // 추가 된다.
/** 제네릭 사용 : <String> **/
List<String> list = new ArrayList<>();
list.add("코딩맨"); // 추가된다.
list.add(1); // 컴파일 오류 발생
잠깐 문제!
// Object는 모든 클래스의 조상이다!
List<Object> objList = new ArrayList<Integer>();
해당 코드가 컴파일 오류가 나는지 안 나는지 확신이 없다면 제네릭(Generic)에 대해 잘 모르는 것이다.
그렇다면 이 블로그로 갈증을 해결하길 바랍니다.
제네릭의 장점
1. 타입의 안정성을 제공한다.
위에 코드를 보면 제네릭 미사용 했을 때 stringList에 String타입의 객체만 넣고 싶은데 숫자 1 도 같이 리스트에 추가되는 것을 볼 수 있다.
이렇게 개발자가 의도하지 않은 타입의 객체가 저장될 수도 있으며,
저장되어서 잘못 추가된 해당 객체를 꺼내 올 때 다른 타입으로 잘못 형변환이 발생할 수 있는 것이다.
그래서 제네릭(Generic)을 사용하여 클래스나, 메서드, 컬렉션 클래스에 내가 원하는 타입만 넣게 되어 타입의 안정성을 제공하게 된다.
2. 타입체크와 형변환을 생략할 수 있으므로 코드가 간결해진다.
제네릭(Generic)을 사용하여 미리 타입을 명시에 주었을 때 해당 타입 데이터만 사용한다.
그러므로 번거롭게 다른 타입이었던 것을 해당 타입으로 변경해 주는 형변환을 줄여주거나 생략할 수 있어서 개발 생산성이 향상한다.
제네릭 타입 표
타입 | 설명 |
<T> | Type , 참조 타입 |
<E> | Element , 요소 |
<K> | Key , 키 |
<V> | Value , 값 |
<N> | Number , 숫자 |
제네릭 클래스의 선언
제네릭 타입 표에서 보고해당 변수를 사용하여 Object를 T, E, K 등으로 나타낼 수 있는 것을 알 수 있다.
Unit 객체를 생성할 때 참조변수와 생성자에 대입된 타입(매개변수화된 타입)이 반드시 일치해야 한다.!
Marine이라는 클래스가 존재한다고 하자!
// Unit 객체 생성
Unit<Marine> marineUnit = new Unit<Marine>();
// 참조변수 : marineUnit
// 생성자 : Unit<Marine>()
맨 위에서 깜짝 문제 냈을 때의 정답이 여기서 알게 되는데,
<> 안에 들어가는 제네릭 타입이 상속 관계여도 일치하지 않으면 오류가 난다는 것이다.
Marine은 FightUnit의 자손이라고 가정하겠다.
Unit<Marine> marineUnit = new Unit<Marine>(); // OK
Unit<FightUnit> unit = new Unit<Marine>(); // Error
Tip) 제네릭을 한 번에 이해하기는 어려으므로 여러 번 다양하게 보고, 사용하면 익숙해질 것이다.
와일드 카드
<? extends T>
와일드 카드의 상한 제한을 둔 것이다.
즉, T와 그 자손들만 가능하며 T의 하위 클래스만 올 수 있다는 말이다.
Number 클래스 하위에 Integer, Double 같은 숫자 클래스들이 있다.
예시 코드
public class ClassName <K extends Number> {
...
}
public class Main {
public static void main(String[] args) {
ClassName<Double> a1 = new ClassName<Double>(); // OK!
ClassName<String> a2 = new ClassName<String>(); // error!
}
}
<? super T>
와일드 카드의 하한 제한을 둔 것이다.
즉, T와 그 조상들만 가능하며 T의 상위 클래스만 올 수 있다는 말이다.
예시 코드
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Number> numbersList = new ArrayList<>();
numbersList.add(10);
numbersList.add(20.5);
numbersList.add(30L);
printList(numbersList, new NumberComparator());
}
public static void printList(List<? extends Number> list, Comparator<? super Number> comparator) {
list.sort(comparator);
for (Number item : list) {
System.out.println(item);
}
}
static class NumberComparator implements Comparator<Number> {
@Override
public int compare(Number num1, Number num2) {
return Double.compare(num1.doubleValue(), num2.doubleValue());
}
}
}
/** 출력
* 10
* 20.5
* 30
*/
코드 설명
- printList 메서드에 Comparator 인터페이스를 추가하여 정렬 기능을 제공한다.
해당 메서드는 List<? extends Number> 타입의 리스트와 Comparator<? super Number>를 인자로 받는다. - NumberComparator 클래스 내부에서는 숫자요소를 비교한다.
Comparator<? super T>를 작성하는 이유
만약에 T가 Animal 클래스이고 Comparator를 사용한다면, Animal의 하위 클래스인 Dog이나 Cat등과 같은 객체들을 비교할 수 있어서 사용됩니다.
즉, 가급적 T의 상위 타입으로 Comparator를 정의함으로써, T 타입과 그 하위 타입을 모두 다룰 수 있게 된다는 것입니다.
제네릭 메서드
클래스와 다르게 반환타입 이전에 <> 인 제네릭 타입을 선언했다.
[접근 제한자] <제네릭 타입> [반환타입] [메소드명]([제네릭타입] [파라미터]) { ... }
public <T> T MethodName(T t){ ... } // 제네릭 메소드
public 옆에 있는 <T>와 MethodName안에 있는 매개변수에 있는 T는 전혀 다른 별개의 것입니다.
그래서 같은 타입 문자 T를 사용해도 같은 것이 아니라는 것에 주의해야 합니다.
사용 예시
예시로 Queue로 구현하여 작성해 보았다. 더 보기를 누르면 코드가 나온다.!
import java.util.LinkedList;
import java.util.Queue;
public class GenericQueue {
// 제네릭한 Queue 메소드
public static <T> void processQueue(Queue<T> queue) {
while (!queue.isEmpty()) {
T element = queue.poll();
System.out.print(element + " ");
}
System.out.println();
}
public static void main(String[] args) {
Queue<Integer> integerQueue = new LinkedList<>();
Queue<Double> doubleQueue = new LinkedList<>();
Queue<String> stringQueue = new LinkedList<>();
// 데이터를 큐에 추가
integerQueue.offer(1);
integerQueue.offer(2);
integerQueue.offer(3);
doubleQueue.offer(1.1);
doubleQueue.offer(2.2);
doubleQueue.offer(3.3);
stringQueue.offer("apple");
stringQueue.offer("banana");
stringQueue.offer("orange");
System.out.println("<= Integer Queue =>");
processQueue(integerQueue);
System.out.println("<= Double Queue =>");
processQueue(doubleQueue);
System.out.println("<= String Queue =>");
processQueue(stringQueue);
}
}
/* 출력!
<= Integer Queue =>
1 2 3
<= Double Queue =>
1.1 2.2 3.3
<= String Queue =>
apple banana orange
*/
감사합니다.
'프로그래밍 언어 > 💫JAVA' 카테고리의 다른 글
[JAVA] Comparator 비교 : 데이터 정렬을 우리가 바꾸는 방법은? (0) | 2023.11.15 |
---|---|
[JAVA] Enum 열거 : 코드의 가독성을 높이는 효과적인 선택 (0) | 2023.11.09 |
[JAVA] Set 컬렉션 : 데이터 중복 제거와 효율적 관리 (0) | 2023.10.30 |
[JAVA] List 컬렉션 : 데이터 접근하고 관리하는 최적의 방법은? (0) | 2023.10.25 |
[JAVA] 자바 : 객체지향 프로그래밍의 핵심과 특징, 초보자를 위한 자바 소개 (0) | 2023.10.19 |
- Total
- Today
- Yesterday
- AJAX
- 개발자
- java
- git
- aws
- 개발환경
- Fetch
- Spring Security
- DBeaver
- 템플릿
- 네트워크
- JavaScript
- 프론트
- 개발블로그
- 개발
- 디자인패턴
- 오라클
- Mac
- 데이터 베이스
- 프로세스
- 비동기
- Front
- 자바스크립트
- 코딩테스트
- spring
- Cors
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |