List <Integer> list = new ArrayList <>();
이 때 <Integer>
과 ArrayList의 <>
은 제네릭 표현식이다.
제네릭 타입은 어떨 때 사용할까?
제네릭 타입은 어떻게 선언할까?
<T>
를 선언해준다.package Generic1
public class man <T> {
private T name;
//이름 필드
private T bloodtype;
//혈액형 필드
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
public T getBloodtype() {
return bloodtype;
}
public void setBloodtype(T bloodtype) {
this.bloodtype = bloodtype;
}
}
man<String> man1 = new man<>();
setter
등을 통해서 값을 넣어줄 때는 위 객체를 생성할 때 지정해준 타입의 값을 넣어주면 된다.package Generic1;
public class mainGe {
public static void main(String[] args) {
man<String> man1 = new man<>();
//새로운 객체 생성시 원하는 타입을 부여하면 된다.
man1.setName("King");
man1.setBloodtype("A");
//선언시 스트링 타입으로 선언하여 스트링 데이터를 입력하였다.
}
}
<>
기호를 추가하고, 타입 파라미터를 기술한 다음 리턴 타입과 매개 타입으로 타입 파라미터를 사용하면 된다.public class info {
public static <T> void info(T t){
}
}
메서드에 넘기고 싶은 타입의 값을 넘겨주면 된다.
Info.info("King");
제한된 타입 파라미터 제네릭
package Generic1;
public class man2{
public static <T extends Number> int compareAge(T age1, T age2) {
int one = age1.intValue();
int two = age2.intValue();
int big = one >= two ? one : two;
return big;
}
}
그러면 지금까지 공부한 제네릭을 통해서 실제 코드를 분석해보자.
실제 코드는 나와 bigsun이 같이 프로젝트를 진행하면서 만들어진 코드인데, 해당 부분은 bigsun이 제작하였다!
아래는 우리 프로젝트의 코드이다.
https://github.com/JNU-econovation/Dotoring-BE
아래 코드는 이번 프로젝트에서 요긴하게 사용했던 공통 API Response를 다룬 코드이다.
공통 API Response는 제네릭에 매우 적합한 객체이다.
그 이유는 API Response로는 String, Long, 객체 타입등 어떠한 타입들이 모두 올 수 있기에 제네릭으로 타입을 지정한다면 매우 유용하게 사용할 수 있기 때문이다.
@Getter
public class ApiResponse<B> extends ResponseEntity<B> {
public ApiResponse(B body, HttpStatus status) {
super(body, status);
}
public ApiResponse(B body, MediaType mediaType, HttpStatus status) {
super(body, status);
this.getHeaders().setContentType(mediaType);
}
@Getter
@AllArgsConstructor
public static class CustomBody<D> implements Serializable {
private Boolean success;
private D response;
private Error error;
}
}
먼저 객체 이름부터 살펴보자.
제네릭 타입은 이름 뒤에 제네릭 타입이 온다고 설명을 했었다. 그렇기에 위 ApiResonse 객체도 이름 뒤에 <B>
로 제네릭 타입이 오는 것을 볼 수 있다.
그러면 여기서 궁금한 것은 B와 D, ? 등이 무슨 차이가 있을까?
위 질문의 답으로 자바 제네릭을 사용할 때 일반적으로 아래와 같이 사용한다고 한다고 한다.
타입 | 설명 |
---|---|
T | Type |
E | Element |
K | Key |
V | Value |
N | Number |
이것을 통해 알 수 있는 것은 어떤 상황에서는 무엇을 써라가 강제된 것이 아닌 각자가 잘 이해할 수 있는 타입으로 제네릭을 선언해주면 된다는 것이다.
우리는 Body를 뜻하는 B와 D를 제네릭 타입으로 사용하였다.
아래의 코드는 위 ApiResponse 객체의 제네릭을 사용하는 코드이다.
@UtilityClass
public class ApiResponseGenerator {
public static ApiResponse<ApiResponse.CustomBody<Void>> success(final HttpStatus status) {
return new ApiResponse<>(new ApiResponse.CustomBody<Void>(true,null,null),status);
}
public static <D> ApiResponse<ApiResponse.CustomBody<D>> success(final D response, final HttpStatus status) {
return new ApiResponse<>(new ApiResponse.CustomBody(true,response,null), status);
}
public static <D> ApiResponse<ApiResponse.CustomBody<D>> success(final D response, final MediaType mediaType, final HttpStatus status) {
return new ApiResponse<>(new ApiResponse.CustomBody(true,response,null),mediaType,status);
}
public static ApiResponse<ApiResponse.CustomBody> fail(String message, String code, final HttpStatus status) {
return new ApiResponse<>(new ApiResponse.CustomBody(false,null,new Error(message,code,status.toString())),status);
}
}
위 코드들에서는 제네릭 메서드들을 사용했는데, 제네릭 메서드는 위에서 이야기 했듯이 매개변수의 타입과 리턴 타입을 제네릭 타입으로 받고자 할 때 사용한다고 하였었다.
public static <D> ApiResponse<ApiResponse.CustomBody<D>> success(final D response, final HttpStatus status) {
return new ApiResponse<>(new ApiResponse.CustomBody(true,response,null), status);
}
먼저 해당 제네릭 static 메서드에서는 매개변수 타입을 제네릭 타입으로 받고있다. 그렇기에 어떤 타입들의 객체가 오더라도 제네릭으로 처리할 수 있다는 것이다.
또한 리턴 타입 또한 제네릭 타입으로 받고 있기에, 어떤 타입으로든 Response를 할 수 있는 것이다.
위 코드들에 대한 정확한 정의는 추후 “우리팀이 ApiResponse를 다룬 방법”이라는 글에서 좀 더 자세히 다뤄보겠다.