[Java] 제네릭 정리 및 활용

devdo·2022년 3월 18일
0

Java

목록 보기
41/59
post-thumbnail

제네릭(Generic) 이란?

제네릭(Generic) 은 클래스 / 인터페이스 / 메서드 등의 타입을 파라미터로 사용할 수 있게 해주는 역할을 한다.


제네릭 장점

  • 코드 재사용성: 다양한 타입에 대해 공통 코드를 사용 가능
  • 컴파일-타임 체크: 컴파일 시에 제네릭 코드 확인
  • 타입 안정성: 불필요한 데이터 추가를 제한
  • 컬렉션에서 사용: 컬렉션에 객체 타입 필요

예시

public class GenericsExample<T> {

    private T value;

    public GenericsExample(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public static <E> boolean contains(List<E> list, E item) {
        return list.contains(item);
    }

}

위의 코드와 같이 클래스 이름 옆에 <T>를 선언해 준 후
필드의 변수 타입을 모두 T로 선언해 주었다.

이후 타입 T는 객체를 생성할 때 해당 타입으로 변경된다.


💡 제네릭(Generic) <T>, <E> 차이

  • T : Type, 어떠한 유형이든 상관없음.
  • ? : 와일드 카드, 모든 타입 허용.
  • E : Element, List<E>: Collection을 말함

주로 사용하는 키워드는 다음과 같다.

  • E - Element

  • K - Key

  • N - Number

  • T - Type

  • V - Value

  • S,U,V etc. - 2nd, 3rd, 4th types

class Data<K, V> {}

  • 실제 코드 예시
public class mainGe {

    public static void main(String[] args) {
        List<String> stringList = List.of("apple", "banana", "orange");
        System.out.println(contains(stringList, "banana")); // true

        GenericsExample<Integer> intExample = new GenericsExample<>(42);
        System.out.println(intExample.getValue()); // 42
    }

}

제네릭 타입 제한

extends를 사용하여 원하는 클래스의 객체만 허용하도록 사용할 수 있습니다.
ex. AnimalPrinter<T extends Animal>


활용예시

SpringBoot를 사용하면 JPA Repository에서 제네릭의 활용 예시를 잘 볼 수 있다.

1) SpringBoot JPARepository 내부 구조

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>
, QueryByExampleExecutor<T> {

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.CrudRepository#findAll()
	 */
	@Override
	List<T> findAll();

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
	 */
	@Override
	List<T> findAll(Sort sort);

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
	 */
	@Override
	List<T> findAllById(Iterable<ID> ids);

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
	 */
	@Override
	<S extends T> List<S> saveAll(Iterable<S> entities);

2) 직접사용

요청, 응답 객체로 활용

  • Req.class
@Data
public class Req<T> {

    private Header header;

    private T resBody;

    @Data
    public static class Header {
        private String responseCode;
    }

}
  • Result.class
@Getter
@AllArgsConstructor
public class Result<T> {

    private Integer code;

    private String message;

    private String detail;

    private T data;		// data 변수는 Result<T> T가 어떤 타입이든 유동적으로 그 T 타입으로 변하게 된다.

}

ResponseCode

@Getter
@AllArgsConstructor
public enum ResponseCode {

    SUCCESS_CODE(1000, "success"),
    
    ...
    
    private final Integer code;
    private final String message;
    

=> 응답값 구현

    @GetMapping("/list")
    @ApiOperation(value = "Paging List")
    public ResponseEntity<?> getAllWithPage(PageRequestDto pageRequestDto) {

        log.info("listPage, PageRequestDto: {}", pageRequestDto);

        Page<ContentDetailDto> contentWithPage = contentService.getAllWithPage(pageRequestDto);
        return ResponseEntity.ok(new Result<>(SUCCESS_CODE.getCode(), SUCCESS_CODE.getMessage(), null, contentWithPage));
    }
    

여기서 마지막 응답값으로 ResponseEntity 내 body 부분에 정리할 수 있는 것을 볼 수 있다.

return ResponseEntity.ok(new Result<>(SUCCESS_CODE.getCode(), SUCCESS_CODE.getMessage(), null, contentWithPage));

🌟최종 예제

  • UnitUtil
public class UnitUtil {

    // 두 bioUnit을 전달받아 체력 hp 가 높은 유닛을 반환. 체력이 같은 경우 둘 중 아무나 반환해도 된다.
    // 제네릭 메서드를 사용해야 한다.
    // 입력하는 유닛의 타입과 반환하는 유닛의 타입이 같아야 한다.

    public static <T extends BioUnit> T compareMaxHp(T unit1, T unit2) {
        if (unit1.getHp() > unit2.getHp()) {
            return unit1;
        } else {
            return unit2;
        }
    }

}
  • BioUnit
public class BioUnit {
    private String name;
    private int hp;

    public BioUnit(String name, int hp) {
        this.name = name;
        this.hp = hp;
    }

    public String getName() {
        return name;
    }

    public int getHp() {
        return hp;
    }

    @Override
    public String toString() {
        return "BioUnit{" +
                "name='" + name + '\'' +
                ", hp=" + hp +
                '}';
    }
}
  • Marine
public class Marine extends BioUnit{

    public Marine(String name, int hp) {
        super(name, hp);
    }


}
  • main
public class UnitUtilTest {

    public static void main(String[] args) {
        Marine m1 = new Marine("마린1", 50);
        Marine m2 = new Marine("마린2", 50);
        Marine resultMarine = UnitUtil.compareMaxHp(m1, m2);
        System.out.println("resultMarine = " + resultMarine);

    }
}


참고

profile
배운 것을 기록합니다.

0개의 댓글