package functional;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.junit.Test;
import org.junit.jupiter.api.DisplayName;
/*
* 출처 : https://codechacha.com/ko/java8-functional-interface/
*/
public class FunctionalInterfaceTest {
public interface FunctionalInterface {
public abstract String returnParam(String text);
}
@Test
@DisplayName("기존 익명 클래스를 이용한 방식")
public void anonymousClassTest() {
String target = "hello, anonymous class";
FunctionalInterface fi = new FunctionalInterface() {
@Override
public String returnParam(String text) {
return text;
}
};
String result = fi.returnParam(target);
assertThat(target).isEqualTo(result);
}
@Test
@DisplayName("Functional Interface 시작")
public void helloFunctionalInterface() {
String target = "hello, functional interface";
FunctionalInterface fi = (text) -> {
return text;
};
String result = fi.returnParam(target);
assertThat(target).isEqualTo(result);
}
/* 함수형 인터페이스를 람다식과 버무려서 사용할 수 있지만,
* 매번 사용할 때마다 함수형 인터페이스를 정의하는 것은 불편할 수 있음.
* 이를 위해 자바에서 라이브러리를 제공함.
*
* java.util.function 에 정의되어 있는 기본 함수형 인터페이스
* 1. Runnable : 인자 없음, 리턴값 없음
* 2. Supplier<T> : 인자 없음, T 타입의 변수를 리턴함
* 3. Consumer<T> : T 타입의 변수를 인자로 받고, 리턴값 없음
* 4. Function<T, R> : T 타입의 변수를 인자로 받고, R 타입의 변수를 리턴함
* 5. Predicate<T> : T 타입의 변수를 인자로 받고, 결과를 boolean 으로 리턴함
*/
@Test
@DisplayName("1. Runnable : 인자 없음, 리턴값 없음")
public void helloRunnable() {
Runnable runnable = () -> System.out.println("hello, runnable");
runnable.run();
}
@Test
@DisplayName("2. Supplier<T> : 인자 없음, T 타입의 변수를 리턴함")
public void helloSupplier() {
String targetString = "hello, supplier";
Supplier<String> getString = () -> targetString;
assertThat(getString.get()).isEqualTo(targetString);
}
@Test
@DisplayName("3. Consumer<T> : T 타입의 변수를 인자로 받고, 리턴값 없음")
public void helloConsumer() {
Consumer<String> printMyNameConsumer = name -> System.out.println(
"My name is " + name + ".");
printMyNameConsumer.accept("glory");
}
@Test
@DisplayName("3. Consumer<T> : andThen 을 이용하면 두 개 이상의 Consumer 를 연속 실행할 수 있음")
public void consumerAndThen() {
Consumer<String> printMyNameConsumer = name -> System.out.println(
"My name is " + name + ".");
Consumer<String> printMyNicknameConsumer = nickname -> System.out.println(
"My nickname is " + nickname + ".");
printMyNameConsumer.andThen(printMyNicknameConsumer).accept("glory");
}
@Test
@DisplayName("4. Function<T, R> : T 타입의 변수를 인자로 받고, R 타입의 변수를 리턴함")
public void helloFunction() {
int target = 3;
Function<Integer, Integer> multiply = val -> val * 2;
assertThat(multiply.apply(target)).isEqualTo(target * 2);
}
@Test
@DisplayName("4. Function<T, R> : compose 로 두 Function 을 조합하여 새 Function 을 만들 수 있음.")
public void functionCompose() {
int target = 3;
Function<Integer, Integer> multiply = (value) -> value * 2;
Function<Integer, Integer> add = (value) -> value + 3;
// 순서 : add -> multiply
Function<Integer, Integer> composedFunction1 = multiply.compose(add);
assertThat(composedFunction1.apply(target)).isEqualTo((target + 3) * 2);
// 순서 : multiply -> add
Function<Integer, Integer> composedFunction2 = add.compose(multiply);
assertThat(composedFunction2.apply(target)).isEqualTo((target * 2) + 3);
}
@Test
@DisplayName("5. Predicate<T> : T 타입의 변수를 인자로 받고, 결과를 boolean 으로 리턴함")
public void helloPredicate() {
Integer tar1 = 10;
Integer tar2 = -10;
Predicate<Integer> isBiggerThanFive = target -> target > 5;
assertThat(isBiggerThanFive.test(tar1)).isTrue();
assertThat(isBiggerThanFive.test(tar2)).isFalse();
}
@Test
@DisplayName("5. Predicate<T> : and, or 메서드를 사용하여 다른 Predicate 와 동시에 검사 가능")
public void predicateOrAnd() {
Integer tar = 10;
Predicate<Integer> isBiggerThanFive = target -> target > 5;
Predicate<Integer> isLowerThanTwenty = target -> target < 20;
Predicate<Integer> isOddNumber = target -> target % 2 == 1;
assertThat(isBiggerThanFive.and(isLowerThanTwenty).test(tar)).isTrue();
assertThat(isBiggerThanFive.or(isOddNumber).test(tar)).isTrue();
}
}