제네릭(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는 객체를 생성할 때 해당 타입으로 변경된다.
<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) 직접사용
요청, 응답 객체로 활용
@Data
public class Req<T> {
private Header header;
private T resBody;
@Data
public static class Header {
private String responseCode;
}
}
@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));
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;
}
}
}
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 +
'}';
}
}
public class Marine extends BioUnit{
public Marine(String name, int hp) {
super(name, hp);
}
}
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);
}
}