본문 바로가기

Java

Java Functional Interface BiPredicate 활용.

BiPredicate<T,U> : boolean test(T t, U u) : input 2개 return boolean



BiPredicate는 funtional 인터페이스중 하나이다.
기본사용법은 Predicate와 동일하지만, 인자값이 하나더 있다.
외에도 매개변수가 2개인 함수형 인터페이스로는 BiConsumerBiFunction 등이 있다.

BiPredicate는 Predicate interface와 사용법이 거의 비슷하여, 대략적으로 이해만 하고 있었는데
실제로 활용을 해보려고 하니 어떠한 경우에 사용해야할지 감이 잡히지 않았다.
그래서 인터넷의 다른소스들을 참고하여 비슷하고 간단한 예제를 하나 만들어보도록 하겠다.

만약 사용자들의 정보가 담긴 List가 있다고 가정한다.
List에는 이름(name)과, 나이(age)를 정보를 갖고있다.
List에서 다음과 같은 데이터를 추출해보자.
1.List에서 name이 park씨이며 성인인 사용자만 추출
2.List에서 name이 park씨이며 미성년자인 사용자만 추출
이런경우 BiPredicate를 사용하지 않는 로직을 만들고 BiPredicate를 사용하여 로직을 개선해보자.

 

사용자정보를 담는 VO객체 생성.

class PersonVO {
 
    private String name;
    private int age;
 
    public PersonVO(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
 

 

먼저 BiPredicate를 사용하지 않는 경우.

1, 2 번에 해당하는 데이터를 추출하기위해 메소드를 각각 만들어서 상황에 맞게 호출한다.

    /* 1.List에서 name이 park씨이며 성인인 사용자만 추출 */
    public static boolean isAdultUserNm(String name, int age) {
        if (name.indexOf("park"!= -1 && age > 18) {
            return true;
        }
        return false;
    }
 
    /* 2.List에서 name이 park씨이며 미성년자인 사용자만 추출 */
    public static boolean isMinorUserNm(String name, int age) {
        if (name.indexOf("park"!= -1 && age < 19) {
            return true;
        }
        return false;
    }
 
    /* 실행. */
    List<PersonVO> list = new ArrayList<>();list.add(new PersonVO("kimInHo",17));
list.add(new PersonVO("parkGiSun",21));list.add(new PersonVO("parkInSu",15));

    List<PersonVO> resultList = new ArrayList<>();
for(PersonVO person:list) {
        // park씨이면서 성인인(age가 18보다큰) 사용자 추출
        if (BiPredicateExample.isAdultUserNm(person.getName(), person.getAge())) {
            resultList.add(person);
        }
        // 성인이 아닌경우 isMinorUserNm 메소드를 호출하여 추출
    }
    // 결과출력
    resultList.stream().forEach(vo-> {
        System.out.println("OLD Version Adult UserNm : " + vo.getName() + ", Age : " + vo.getAge());
    });

아래와 같이 이름이 pack씨인 만18세 이상 성인인 parkGiSun이 정상적으로 추출되었다.

출력

OLD Version Adult UserNm : parkGiSun, Age : 21

 

1,2 메소드를 하나의 메소드에 통합하고, 어떤 처리를 할지 구분값을 받아서 처리할 수도 있겠다.

1,2 번 통합

public static boolean isAdultUserNmGubun(String name, int age, String type) {

    if ("adult".equals(type)) {
        if (name.indexOf("park"!= -1 && age > 18) {
            return true;
        }
    } else {
        if (name.indexOf("park"!= -1 && age < 19) {
            return true;
        }
    }
    return false;
}

 

위와 같이 처리하는 경우는 여러가지로 업무로직이 변경될 때마다 로직을 손봐야한다.

예를들어 이름이 park씨에서 kim씨로 변경될 수도있고, 나이가 10세 미만으로 변경될 가능성도 있다.

이렇게 변경이 있을때 마다 소스코드를 수정해야하는 불편함이 있다.

  

이제 BiPredicate를 사용하여 개선해보겠다.

BiPredicate를 구현한 lamda express와 사용자정보 리스트를 받아서 추출한 데이터를 리턴하는 메소드를 생성한다.

/*
 * 참고 : <T extends PersonVO> PersonVO 또는 하위객체 와일드카드 t가 어떤 객체인지 모르기 때문에 PersonVO
 * type의 객체라는걸 알려주기위해 메소드 정의부분의 static 옆에 extends PersonVO를 명시해주어야 PersonVO의
 * 메소드를 사용가능하다.
 */
public static <extends PersonVO> List<T> filter(BiPredicate<String, Integer> biPredi, List<T> list) {
    List<T> returnList = new ArrayList<>();
    for (T t : list) {
        if (biPredi.test(t.getName(), t.getAge())) { // test 두개의 인자값을 선언된 람다식으로 체크하여 true or false를 리턴한다.
            returnList.add(t);
        }
    }
    return returnList;
}

실행

    // name이 park이고 18세 이상 성인추출 - 한줄로 간편히 끝난다.
    resultList = BiPredicateExample.filter((name, age) -> name.indexOf("park"!= -1 && age > 18, list);
    // 출력
    resultList.stream().forEach(vo -> {
     System.out.println("NEW Version Adult UserNm : " + vo.getName() + ", Age : " + vo.getAge());
    });

// NEW Version Adult UserNm : parkGiSun, Age : 21

 

 

장점 : BiPredicate를 사용하는 경우는 추출해야하는 name이 변경되거나 age변경 또는 age의 크고 적음,

같거나 큼 등[ >, <, >=, <=] 업무로직이 변경되어도 메소드 내용을 변경하지않고 실행코드만 변경하여 간단히 사용이 가능한 장점이 있다.

  

  

코드를 막상 작성하고나니 Stream API를 사용하여 List를 stream으로 변환 후 filter를 사용하는 방법으로도 쉽게 추출이 가능하다.

BiPredicate를 사용하는 예제가 적절하지 않은 건지는 한번 검토해 봐야겠다.