반응형
제네릭스(Generics)
정의
- 다양한 타입의 객체를 지원하는 클래스나 인터페이스 또는 메서드를 정의하는 방법
- 똑같은 동작을 하는 클래스나 메서드를 여러 번 만들 필요 없이 딱 한 번만 정의한 후 여러 타입에 대해서 지원할 수 있도록 해주는 것
[예시] 제네릭스 사용 전
public class Main {
public static void main(String[] args) {
//제네릭스
int[] iArray = {1, 2, 3, 4, 5};
double[] dArray = {1.0, 2.0, 3.0, 4.0, 5.0};
String[] sArray = {"A", "B", "C", "D", "E"};
printIntArray(iArray);
printDoubleArray(dArray);
printStringArray(sArray);
}
private static void printStringArray(String[] sArray) {
for (String s : sArray) {
System.out.print(s + " ");
}
System.out.println();
}
private static void printDoubleArray(double[] dArray) {
for (double d : dArray) {
System.out.print(d + " ");
}
System.out.println();
}
private static void printIntArray(int[] iArray) {
for (int i : iArray) {
System.out.print(i + " "); //1 2 3 4 5
}
System.out.println();
}
}
- 자료형만 다르고 각 메서드의 코드가 반복된다.
- 만약 long, float, boolean 등의 자료형이 추가된다면 서로 다른 자료형을 지원하는 메서드를 추가해야 한다.
- 이럴 때 제네릭스를 이용할 수 있다.
[예시] 제네릭스 사용 후
public class Main {
public static void main(String[] args) {
//제네릭스
Integer[] iArray = {1, 2, 3, 4, 5};
Double[] dArray = {1.0, 2.0, 3.0, 4.0, 5.0};
String[] sArray = {"A", "B", "C", "D", "E"};
printAnyArray(iArray);
printAnyArray(dArray);
printAnyArray(sArray);
}
private static <T> void printAnyArray(T[] array) {
for (T t : array) {
System.out.print(t + " ");
}
System.out.println();
}
}
- 전달받는 값이 어떤 값인지 모르기 때문에 static과 void 사이에 <T>를 적어주고 전달받는 값도 T[] array과 같이 받는다.
- 이때 T는 Type을 의미하고 일반적으로 T를 적지만 꼭 T가 아니더라도 다른 값을 넣어도 쌍만 이루어진다면 상관없다.
- T(Type), K(Key), V(Value), E(Element)와 같이 쓴다.
- 출력을 위해 printAnyArray(iArray), printAnyArray(dArray)를 작성하면 에러 발생
- 제네릭스에서 지원하는 것은 객체. 기본 자료형(int, double, float ...)과 참조 자료형(Integer, Double, Float ...)
- 이런것들은 제네릭스 타입으로 바로 사용 불가
- 래퍼 클래스를 이용해야 함
- int → Integer.parseInt()
- double → Double.parseDouble()
제네릭 클래스(Generic Class)
[예시] 커피숍 주문 - 제네릭 클래스 적용 전
Main
public class Main {
public static void main(String[] args) {
//제네릭 클래스
CoffeeByNumber c1 = new CoffeeByNumber(33);
c1.ready();
CoffeeByNickname c2 = new CoffeeByNickname("prao");
c2.ready();
}
}
CoffeeByNumber
public class CoffeeByNumber {
public int waitingNumber;
public CoffeeByNumber(int waitingNumber) {
this.waitingNumber = waitingNumber;
}
public void ready() {
System.out.println("커피 준비 완료 : " + waitingNumber);
}
}
CoffeeByNickName
public class CoffeeByNickname {
public String nickname;
public CoffeeByNickname(String nickname) {
this.nickname = nickname;
}
public void ready() {
System.out.println("커피 준비 완료 : " + nickname);
}
}
- CoffeeByNumber와 CoffeeByNickname의 동작은 같다.
- 어떤 값을 전달받고 ready 메서드를 호출하면 값을 출력하는 형태
- 자료형이 바뀌면 매번 새로운 클래스를 생성해야 함
모든 자료형의 조상인 Object를 사용하면?
Main
public class Main {
public static void main(String[] args) {
//제네릭 클래스
CoffeeByName c3 = new CoffeeByName(34);
c3.ready();
CoffeeByName c4 = new CoffeeByName("ugirin");
c4.ready();
}
}
CoffeeByName
public class CoffeeByName {
public Object name;
public CoffeeByName(Object name) {
this.name = name;
}
public void ready() {
System.out.println("커피 준비 완료 : " + name);
}
}
- 정수, 문자 상관없이 출력 성공
- 사용자가 실수를 하지 않으면 괜찮지만 코드를 실행해보기 전까지 에러를 알 수 없음
- 위 코드와 같이 주문 고객 이름에 실수로 주문 고객 번호를 넣어도 에러가 발생하지 않는 문제 발생
제네릭 클래스 적용
Coffee
public class Coffee<T> {
public T name;
public Coffee(T name) {
this.name = name;
}
public void ready() {
System.out.println("커피 준비 완료 : " + name);
}
}
Main
public class Main {
public static void main(String[] args) {
//제네릭 클래스
Coffee<Integer> c5 = new Coffee<>(35);
c5.ready();
Coffee<String> c6 = new Coffee<>("손흥민");
c6.ready();
}
}
- 제네릭 클래스 사용 시 자료형을 먼저 설정하였기 때문에 설정한 자료형만 넣을 수 있어서 실수를 방지 가능
커피숍 이용 고객의 데이터를 클래스로 관리
CoffeeByUser
public class CoffeeByUser<T extends User> {
public T user;
public CoffeeByUser(T user) {
this.user = user;
}
public void ready() {
System.out.println("커피 준비 완료 : " + user.name);
user.addPoint();
}
}
- 원하는 형태의 클래스 객체만 받음
- 어떤 형태의 타입을 쓰던 상관없지만 반드시 User라는 클래스를 상속하는 T를 써야한다.
User
public class User {
public String name;
public User(String name) {
this.name = name;
}
public void addPoint() {
System.out.println(this.name + "님 포인트 적립되었습니다.");
}
}
VipUser
public class VIPUser extends User {
public VIPUser(String name) {
super("VIP " + name);
}
}
Main
public class Main {
public static void main(String[] args) {
//제네릭 클래스
CoffeeByUser<User> c7 = new CoffeeByUser<>(new User("강호동"));
c7.ready();
CoffeeByUser<User> c8 = new CoffeeByUser<>(new VIPUser("유재석"));
c8.ready();
}
}
- new User()을 통해 객체를 생성하고 바로 CoffeeByUser에 객체를 넣음
결과
커피 준비 완료 : 강호동
강호동님 포인트 적립되었습니다.
커피 준비 완료 : VIP 유재석
VIP 유재석님 포인트 적립되었습니다.
두 개 이상의 데이터를 처리하는 메서드
Main
public class Main {
public static void main(String[] args) {
//제네릭 클래스
orderCoffee("김영철");
orderCoffee(36);
orderCoffee("이동국", "아메리카노");
orderCoffee(37, "카페라떼");
}
public static <T> void orderCoffee(T name) {
System.out.println("커피 준비 완료 : " + name);
}
public static <T, V> void orderCoffee(T name, V coffee) {
System.out.println(coffee + " 준비 완료 : " + name);
}
}
결과
커피 준비 완료 : 김영철
커피 준비 완료 : 36
아메리카노 준비 완료 : 이동국
카페라떼 준비 완료 : 37
Wrapper 클래스
래퍼(Wrapper) 클래스란?
- 기본 자료형(int, double, float, char)을 객체 형태로 만들어서 사용할 수 있도록 함
public class Main {
public static void main(String[] args) {
//래퍼(Wrapper) 클래스
//int, double, float, char
Integer i = 123; // int i = 123;
Double d = 1.0; // double d = 1.0;
Character c = 'A'; // char c = 'A';
System.out.println(i);
System.out.println(d);
System.out.println(c);
System.out.println("-----------------");
System.out.println(i.intValue()); // 정수값 출력
System.out.println(d.intValue()); // 정수값 출력
System.out.println(c.charValue()); // char값 출력
System.out.println("-----------------");
결과
123
1.0
A
-----------------
123
1
A
-----------------
123
참고자료
반응형
'Java' 카테고리의 다른 글
[Java] 익명클래스, 람다와 스트림 (1) | 2023.10.11 |
---|---|
[Java] 컬렉션 프레임워크(List, Set, Map, Iterator) (0) | 2023.10.11 |
[Java] 람다식(Lambda Expression)과 함수형 인터페이스(Functional Interface) (0) | 2023.09.10 |
[Java] 함수형 프로그래밍(Functional Programming) (0) | 2023.09.08 |
[Java] 예외 처리 (0) | 2023.08.26 |