제네릭 <T>

Kevin·2023년 8월 3일
1

JAVA

목록 보기
3/4
post-thumbnail

제네릭 타입이 뭘까?

List <Integer> list =  new ArrayList <>();
  • 이 때 <Integer> 과 ArrayList의 <>은 제네릭 표현식이다.

  • 제네릭 타입은 어떨 때 사용할까?

    • 객체를 생성할 때 해당 객체의 타입을 동적으로 결정하기 위해서 사용한다.
    • 즉 객체를 생성할 때 int나 String 같은 타입에 종속되지 않기 위해서 사용한다는 것이다.
  • 제네릭 타입은 어떻게 선언할까?

    • 클래스, 인터페이스, 메서드 네임 뒤에 <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");

제네릭의 범위를 지정할 수 있을까??

  • 있다.
    • 제한된 타입 파라미터 제네릭
    • 예를 들어서 age 변수에는 숫자 타입만이 들어갈 수 있는데, 이 경우 제네릭으로 표현할 시 String이나 다른 타입으로 지정될 위험이 존재한다.
    • 타입의 상위 타입으로 지정하여서, 제네렉의 범위를 상위 타입 이내의 타입들로 지정
      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, 객체 타입등 어떠한 타입들이 모두 올 수 있기에 제네릭으로 타입을 지정한다면 매우 유용하게 사용할 수 있기 때문이다.


  • ApiResponse.class
    
    @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, ? 등이 무슨 차이가 있을까?

위 질문의 답으로 자바 제네릭을 사용할 때 일반적으로 아래와 같이 사용한다고 한다고 한다.

타입설명
TType
EElement
KKey
VValue
NNumber

이것을 통해 알 수 있는 것은 어떤 상황에서는 무엇을 써라가 강제된 것이 아닌 각자가 잘 이해할 수 있는 타입으로 제네릭을 선언해주면 된다는 것이다.

우리는 Body를 뜻하는 B와 D를 제네릭 타입으로 사용하였다.

아래의 코드는 위 ApiResponse 객체의 제네릭을 사용하는 코드이다.

  • ApiResponseGenerator.class
    
    @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를 다룬 방법”이라는 글에서 좀 더 자세히 다뤄보겠다.

profile
Hello, World! \n

0개의 댓글