Oracle에 공식 홈페이지에 나와 있는 Predicate에 대한 명세이다.
Predicate<T>
와 같이 타입 매개변수를 가진다.test(T t)
하나이고 나머지 메소드들은 static, default 메소드다.
boolean
타입 메소드로매개 변수 하나
를 받아 매개 변수가 논리식에 맞는지 반환한다.
public class User {
private final String name;
public User(final String name, final int age) {
this.name = name;
}
public boolean hasName(final String name) {
return this.name.equals(name);
}
}
@Test
void test_Test() {
final Predicate<User> namePredicate = (user) -> user.hasName("Chris");
assertThat(namePredicate.test(new User("Chris"))).isTrue(); //pass
assertThat(namePredicate.test(new User("John"))).isFalse(); //pass
}
위와 같이 lambda로 처리할 수 있고 매개변수 값 하나를 받아 값이 식을 만족하는지 반환한다.
default 메소드로 and 메소드로 다수의 Predicate들을 Chaning식으로 연결
할 수 있다.
다른 Predicate를 받아 자신의 test 결과 값과 받아온 Predicate의 test의 결과 값을 &&
연산자로 계산해 반환한다.
public class User {
private final String name;
public User(final String name, final int age) {
this.name = name;
this.age = age;
}
public boolean hasNameStartingWith(final String word) {
return name.startsWith(word);
}
public boolean hasNameLongerThan(final int nameLength) {
return name.length() > nameLength;
}
}
class UserTest {
@Test
void and_Test() {
final Predicate<User> firstWordPredicate = (user) -> user.hasNameStartingWith("C");
final Predicate<User> nameLengthPredicate = (user) -> user.hasNameLongerThan(6);
final User first = new User("Chris");
final User second = new User("Christian");
final boolean firstActual = firstWordPredicate.and(nameLengthPredicate).test(first);
final boolean secondActual = firstWordPredicate.and(nameLengthPredicate).test(second);
assertThat(secondActual).isTrue(); //pass
assertThat(firstActual).isFalse(); //pass
}
}
위와 같이 체이닝으로 연결된 모든 Predicate에서 true
가 반환되어야 한다.
default 메소드로, 쉽게 말해서 ! 연산이라고 생각하면 된다. true면 false를 false면 true를 반환한다.
public class User {
private final String name;
public User(final String name, final int age) {
this.name = name;
this.age = age;
}
public boolean hasNameStartingWith(final String word) {
return name.startsWith(word);
}
public boolean hasNameLongerThan(final int nameLength) {
return name.length() > nameLength;
}
}
class UserTest {
@Test
void negate_Test() {
final Predicate<User> namePredicate = (user) -> user.hasNameStartingWith("C");
final User chris = new User("Chris");
final boolean negatedActual = namePredicate.negate().test(chris);
final boolean notNegatedActual = namePredicate.test(chris);
assertThat(notNegatedActual).isTrue(); //pass
assertThat(negatedActual).isFalse(); //pass
}
}
default 메소드로, and 메소드와 같이 체이닝
방식으로 쓰이고 연결된 모든 Predicate
의 반환 값을 or 연산자로 계산해 반환한다.
public class User {
private final String name;
public User(final String name, final int age) {
this.name = name;
this.age = age;
}
public boolean hasNameStartingWith(final String word) {
return name.startsWith(word);
}
public boolean hasNameLongerThan(final int nameLength) {
return name.length() > nameLength;
}
}
class UserTest {
@Test
void or_Test() {
final Predicate<User> firstWordPredicate = (user) -> user.hasNameStartingWith("C");
final Predicate<User> nameLengthPredicate = (user) -> user.hasNameLongerThan(6);
final User first = new User("Chris");
final boolean actual = firstWordPredicate.or(nameLengthPredicate).test(first);
assertThat(actual).isTrue(); //pass
}
}
Chris는 C로 시작하지만(true), 이름의 길이가 6보다 길지 않다(false)
이 두 결과를 or 연산자로 계산하면 true가 반환된다.
isEqual은 static 연산자로 equals
라고 생각하면 된다.
public class User {
private final String name;
public User(final String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
class UserTest {
@Test
void and_Test() {
final User first = new User("Chris");
final User second = new User("Chris");
final Predicate<User> chrisPredicate = Predicate.isEqual(first);
final boolean actual = chrisPredicate.test(second);
assertThat(actual).isTrue(); //pass
}
}
Predicate.isEqual(first); → first.equals() 라고 생각하면 된다.
equals가 override 되있지 않으면 의도대로 작동하지 않는다.
default 메소드로 java 11에 추가되었다. negate와 같이 논리식 결과의 반대를 반환하지만 사용방식이 다르다.
class UserTest {
@Test
void and_Test() {
final User first = new User("Chris");
final User second = new User("Chris");
final Predicate<User> chrisPredicate = Predicate.isEqual(first);
final Predicate<User> notPredicate = Predicate.not(chrisPredicate);
final boolean actual = notPredicate.test(second);
assertThat(actual).isFalse();
}
}
Predicate<Integer>
Predicate<Long>
Predicate<Double>
과 동일하게 동작한다.
그런데 IntPredicate
, LongPredicate
, DoublePredicate
이 존재하는 이유는 아래와 같다.
final List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
우리는 Integer(Wrapper) 타입의 컬렉션에 int(primitive)를 넣어서 사용할 수 있다. Auto-Boxing
덕분이다.
여기서 주목할 점은 결국 Auto-Boxing을 해줘야한다는 것이다.
Predicate<Integer>
는 가능한데 Predicate<int>
는 불가능 하다.
따라서 정수에 대한 Predicate Auto-Boxing이 일어난다. 이런 불필요한 과정을 없애기 위해서 존재하는 것이다.
❗️ 또한 Predicate와는 달리 and()
, negate()
, or()
, test()
이 네가지 메소드만 제공한다.
not과 isEqaul은 제공하지 않는다. 생각해보면 primitive 타입이 isEqual은 당연히 필요없을 것이다.
not은 java 11에 Predicate와 같이 추가해주지 않았을까?
BiPredicate<T, U> 는 2개의 인자
를 받는 Predicate라고 생각하면 된다.
and()
, negate()
, or()
, test()
이 네가지 메소드만 제공한다.
isEqual은 역시 T, U가 다른 타입이기 때문에 필요없다. (같은 타입이 들어올 수도 있지만 말이야)
public class User {
private final Name name;
public User(final String name) {
this.name = new Name(name);
}
public boolean hasName(final Name name) {
return this.name.equals(name); //Name은 equals가 override 되어있다.
}
}
class UserTest {
@Test
void and_Test() {
final User chris = new User("Chris");
final Name chrisName = new Name("Chris");
final BiPredicate<User, Name> biPredicate = (user, name) -> user.hasName(name);
final boolean actual = biPredicate.test(chris, chrisName);
assertThat(actual).isTrue(); //pass
}
}
위와 같이 test 메소드를 사용하면 되고 나머지 메소드들은 동일하다.
https://codechacha.com/ko/java8-predicate-example/
https://stackoverflow.com/questions/37813271/why-different-predicate-interfaces-n-java-8