[예제 코드]
1. Bean
package generics_v1;
import java.time.LocalDate;
public class FruitBean {
private int price;
private LocalDate entryDate;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public LocalDate getEntryDate() {
return entryDate;
}
public void setEntryDate(LocalDate entryDate) {
this.entryDate = entryDate;
}
}
package generics_v1;
public class AppleBean extends FruitBean{
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
package generics_v1;
public class BananaBean extends FruitBean{
private String country;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
CoffeeBean은 FruitBean을 상속받지 않았다.
package generics_v1;
public class CoffeeBean {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
2. 전체 가격 합계를 구하는 메서드
FruitBean을 상속 받는 Bean만 사용할 수 있도록 <T extends FruitBean> 상한경계를 사용했다.
package generics_v1;
import java.util.List;
public class Calculator {
//제네릭 타입 매개변수의 바운드는 메서드 선언 부분에서 지정
public <T extends FruitBean> int totalPriceSum(List<T> list){
int totalSum = 0;
totalSum = list.stream().mapToInt(FruitBean::getPrice).sum();
return totalSum;
}
}
3. 테스트 코드
package study;
import generics_v1.AppleBean;
import generics_v1.BananaBean;
import generics_v1.Calculator;
import generics_v1.CoffeeBean;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class GenericsTest {
public Calculator cal;
@BeforeEach
public void setUp(){
cal = new Calculator();
}
@DisplayName("AppleBean Test")
@Test
public void appleTotalSum(){
List<AppleBean> list = new ArrayList<>();
for(int i = 100; i < 400; i=i+100){
AppleBean bean = new AppleBean();
bean.setPrice(i);
list.add(bean);
}
assertEquals(600, cal.totalPriceSum(list));
}
@DisplayName("BananaBean Test")
@Test
public void bananaTotalSum(){
List<BananaBean> list = new ArrayList<>();
for(int i = 100; i < 500; i=i+100){
BananaBean bean = new BananaBean();
bean.setPrice(i);
list.add(bean);
}
assertEquals(1000, cal.totalPriceSum(list));
}
@DisplayName("No FruitBean Test")
@Test
public void CoffeeTotalSum(){
List<CoffeeBean> list = new ArrayList<>();
for(int i = 100; i < 500; i=i+100){
CoffeeBean bean = new CoffeeBean();
bean.setPrice(i);
list.add(bean);
}
//컴파일 시점에 Error
//reason: no instance(s) of type variable(s) exist so that CoffeeBean conforms to FruitBean
assertEquals(1000, cal.totalPriceSum(list));
}
}
의도했던 대로 FruitBean을 상속받는 Bean만 사용이 가능해서 CoffeeTotalSum 메서드에서 Calculator의 totalPriceSum을 사용하지 못해 컴파일 에러가 발생했다.
[느낀점]
제네릭 메서드 예제를 만들며 느낀 점을 공유하려고 한다.
1. 컴파일시에 에러를 발생시키는 코드를 만들자.
- 자바를 공부할 때마다 컴파일시에 에러를 발생시키는게 장점으로 나온 개념들이 많았다. 이론을 공부할 때는 필요성에 대해 느끼지 못했지만, 예제 코드나 실무에서 코드를 만들 때 컴파일 시에 에러가 발생하면 이유를 알고 바로 수정이 가능해서 좋다.
2. 제네릭 메서드의 상한경계, 하한경계를 표시할 땐 메서드 선언 부분에 표시한다.
1) 제네릭 타입 매개변수에 제한을 설정하려면 메서드 선언 부분에 <T extends FruitBean>를 사용한다.
2) 메서드 매개변수로 List<T>를 사용한다.
틀린 예제
public <T> int totalPriceSumError(List<T extends FruitBean> list){
int totalSum = 0;
totalSum = list.stream().mapToInt(FruitBean::getPrice).sum();
return totalSum;
}
올바른 예제
public <T extends FruitBean> int totalPriceSum(List<T> list){
int totalSum = 0;
totalSum = list.stream().mapToInt(FruitBean::getPrice).sum();
return totalSum;
}
FruitBean을 상속 받으면서 price 필드가 있는 것을 보장해야한다. 그래야 메서드에서 에러가 발생하지 않는다.
3. 간단한 로직 검증할 때 테스트 코드를 사용하자.
- 아직 테스트코드를 잘 활용하지는 못하지만, 이번 예제를 만들면서 내가 예상했던 값이 나오는지 로직 확인할 때 사용했다. 간단하게 빠르게 확인 할 수 있어서 좋았다. 다양하게 테스트 코드를 작성하는 방법을 알아보자.
[참고]
[Java] 제네릭의 이해2
제네릭의 이해 2에서는 제네릭 타입 제한과 와일드카드에 대해서 다뤄보려고 한다. [제네릭의 이해 1 ] 링크는 아래 참고.. [Java] 제네릭의 이해1이번에 기능 개발에 참고하고 싶은 코드
run-ran-run-ant.tistory.com
'개발 > 개발노트' 카테고리의 다른 글
API, 라이브러리, 프레임워크 차이점 (0) | 2024.08.13 |
---|---|
[DB 형상관리] SpringBoot에 Flyway 도입 (0) | 2024.04.01 |
[Network] application/x-www-form-urlencoded 와 application/json의 흐름 (0) | 2024.03.28 |
[Network] 나는 지금 RESTful API를 만들고 있을까? (1) | 2024.02.01 |
[JPA] DTO와 Entity를 분리하는 이유 (1) | 2024.01.26 |