Home BiPredicate, Function
Post
Cancel

BiPredicate, Function

Java가 제공하는 함수형 인터페이스에는 java.util.function에서 제공하는 것들이 존재한다.

크게 5가지로

  • Consumer
  • Supplier
  • Function
  • Operator
  • Predicate

이 중 이 포스트에서는 Predicate 계열의 BiPredicate, Function계열의 Function에 대해 소개하려고 한다.

Bi가 붙으면 2개의 매개변수를 받아 반환값을 전달한다고 보면 된다.

ex) BiFunction - 두 개의 매개변수를 받는 함수에 대해 동작.

BiPredicate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@FunctionalInterface
public interface BiPredicate<T, U> {

  boolean test(T t, U u);

  default BiPredicate<T, U> and(BiPredicate<? super T, ? super U> other) {
    Objects.requireNonNull(other);
    return (T t, U u) -> test(t, u) && other.test(t, u);
  }

  default BiPredicate<T, U> negate() {
    return (T t, U u) -> !test(t, u);
  }

  default BiPredicate<T, U> or(BiPredicate<? super T, ? super U> other) {
    Objects.requireNonNull(other);
    return (T t, U u) -> test(t, u) || other.test(t, u);
  }
}

BiPredicate Interface는 Java에서 함수형 프로그래밍을 구현하기 위해 Java 버전 1.8부터 도입된 함수형 인터페이스이다.

제네릭 타입인 두 개의 매개변수를 전달받아 특정 작업을 수행 후 Boolean 타입의 값을 반환하는 작업을 수행할 때 사용된다.

  • T: 첫 번째 매개변수 타입
  • U: 두 번째 매개변수 타입

BiPredicate 인터페이스 내부에는 한 개의 추상 메서드와 세 개의 디폴트 메서드가 존재한다.

람다 표현식을 사용하면 추상 메서드인 test() 메서드를 구현하기 위한 클래스를 정의할 필요 없으며, BiPredicate 타입의 객체에 할당된 람다 표현식은 test() 메서드를 구현하기 위해 사용된다.

1
2
BiPredicate<Integer, Boolean> check = (count, isMatch) -> count == 5 && !isMatch;
//(count, isMatch) -> count == 5 && !isMatch 가 test() 메서드를 구현하기 위해 사용된다.

추상 메서드 - test()

test() 메서드는 제네릭 타입인 두 개의 매개변수를 전달받아 특정 작업을 수행 후 Boolean 값을 반환한다.

1
boolean test(T t, U u);

test() 메서드를 구현하기 위해 두 개의 매개변수를 가지며, Boolean 타입의 값을 반환하는 람다 표현식을 BiPredicate 타입의 객체에 할당한다. 그러면 람다 표현식은 test() 메서드를 구현하는데 사용된다.

1
2
3
4
5
BiPredicate<String, String> predicate= (str1, str2) -> str1.equals(str2);

System.out.println(predicate.test("abc", "abd");

//false

디폴트 메서드

and()

디폴트 메서드인 and() 메서드는 BiPredicate 타입의 객체를 매개변수로 가진다.

1
2
3
4
default BiPredicate<T, U> and(BiPredicate<? super T, ? super U> other) {
    Objects.requireNonNull(other);
    return (T t, U u) -> test(t, u) && other.test(t, u);
}

test() 메서드 반환 결과와 and() 메서드의 매개변수로 전달된 BiPredicate 객체의 test() 메서드 반환 결과에 대해 and 연산을 수행한다.

and() 메서드의 매개변수로 BiPredicate 객체 대신 두 개의 매개변수를 가지며, Boolean 타입의 값을 반환하는 람다 표현식을 전달할 수도 있다.

1
2
3
4
5
6
7
8
BiPredicate<Integer, Integer> predicate1 = (num1, num2) -> num1 > num2;
BiPredicate<Integer, Integer> predicate2 = (num1, num2) -> num1 == num2;

System.out.println(predicate1.and(predicate2).test(1, 1));
System.out.println(predicate1.and((num1, num2) -> num1 < num2).test(1, 1);

//true
//false

negate()

디폴트 메서드 negate() 메서드는 매개변수를 가지지 않으며, test() 메서드의 반환 결과를 부정한다.

1
2
3
default BiPredicate<T, U> negate() {
    return (T t, U u) -> !test(t, u);
}
1
2
3
4
5
6
BiPredicate<Integer, Integer> predicate = (num1, num2) -> (num1 + num2) > 100;

System.out.println(predicate.negate().test(50, 30));

//true
//100보다 작아 false를 반환하는게 맞지만 negate()가 있기 때문에 부정 결과인 true가 반환된다.

or()

디폴트 메서드 or() 메서드는 and() 메서드와 매개변수가 동일하다. 따라서 BiPredicate 타입의 객체를 전달하거나 람다식을 전달할 수 있다.

1
2
3
4
default BiPredicate<T, U> or(BiPredicate<? super T, ? super U> other) {
  Objects.requireNonNull(other);
  return (T t, U u) -> test(t, u) || other.test(t, u);
}

test() 메서드 반환 결과와 or() 메서드의 매개변수로 전달된 BiPredicate 객체의 test() 메서드 반환 결과에 대해 or 연산을 수행한다.

1
2
3
4
5
6
BiPredicate<Integer, Integer> predicate1 = (num1, num2) -> num1 > num2;
BiPredicate<Integer, Integer> predicate2 = (num1, num2) -> num1 == num2;

System.out.println(predicate1.or(predicate2).test(10, 10);

//true

Function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@FunctionalInterface  
public interface Function<T, R> {  
  
	R apply(T t);  
	   
	default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {  
		Objects.requireNonNull(before);  
		return (V v) -> apply(before.apply(v));  
	}  
	  
	default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {  
		Objects.requireNonNull(after);  
		return (T t) -> after.apply(apply(t));  
	}  
	  
	static <T> Function<T, T> identity() {  
		return t -> t;  
	}  
}

함수형 인터페이스이다.

  • T: 함수에 대한 입력 유형
  • R: 함수 결과의 유형

T타입 인자를 받아 R타입을 리턴한다.

1
2
3
4
Function<String, Object> function = Person::create;

위의 예시로 들면 Person.create(name) 메서드는 매개변수로 String name이 필요한 메서드이다.
따라서 String - T의 매개변수를 받고 Person을 생성해 반환(Object - R를 반환)하는 것이다.

Function 인터페이스 내부에는 한 개의 추상 메서드와 두 개의 디폴트 메서드, 그리고 한 개의 정적 메서드가 존재한다.

BiPredicate와 마찬가지로 람다 표현식의 사용이 가능하다.

추상 메서드 - apply()

1
R apply(T t);

제네릭 타입인 한 개의 매개변수를 전달받아 특정 작업을 수행 후 값을 반환한다.

1
2
3
4
5
Function<Integer, String> functionAdd = (num) -> Integer.toString(num + 100);

System.out.println(functionAdd.apply(10));

//110

디폴트 메서드

compose()

1
2
3
4
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
  Objects.requireNonNull(before);
  return (V v) -> apply(before.apply(v));
}

compose() 메서드는 매개변수로 전달받은 Function 객체의 apply() 메서드를 호출 후 반환 결과를 apply() 메서드에 전달한다.

1
2
3
4
5
6
7
Function<Integer, Integer> functionAdd = (num) -> num + 100;
Function<Integer, Integer> functionMultiple = (num) -> num * 10;

System.out.println(functionAdd.compose(functionMultiple).apply(10));

//(10 * 10) + 100
//200

andThen()

1
2
3
4
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
  Objects.requireNonNull(after);
  return (T t) -> after.apply(apply(t));
}

andThen() 메서드는 apply() 메서드 호출 후 반환 결과를 매개변수로 전달받은 Function 객체의 apply() 메서드에 전달한다.

1
2
3
4
5
Function<Integer, Integer> functionAdd = (num) -> num + 100;
Function<Integer, Integer> functionMultiple = (num) -> num * 10;

System.out.println(functionAdd.andThen(functionMultiple).apply(10));
// 1100
  • compose(): compose()에 전달한 Fucntion의 apply 결과를 전달 후 먼저의 함수 apply.
  • andThen(): andThen()에 전달한 Function에게 먼저의 apply 값을 전달 후 andThen 메서드 apply.
This post is licensed under CC BY 4.0 by the author.