함수형 인터페이스란 하나의 추상 메서드를 가지는 인터페이스
를 말한다.
public interface MyInterface {
public abstract void max(int a, int b);
}
본래 인터페이스는 다음과 같이 구현한다.
public class MyClass implements MyInterface {
@Override
public void max(int a, int b) {
return a > b ? a : b;
}
}
왜 단 하나의 추상 메서드를 가지는 인터페이스를 선언할까? 그건 자바 8에 추가된 람다식
을 표현하기 위해 사용된다. 먼저, 함수형 인터페이스를 별도의 클래스로 구현하는 것이 아닌 익명 클래스로 구현하면 다음과 같이 생성할 수 있다.
Myfunction f = new MyFunction() {
public int max(int a, int b) {
return a > b ? a : b;
}
};
int big = f.max(5, 3);
위 코드를 람다식을 활용하여 축약하면 다음과 같이 간략하게 표현할 수 있다.
MyFunction f = (int a, int b) -> a > b ? a : b;
int big = f.max(5, 3);
왜 자바는 람다식 표현을 인터페이스로 구현했을까? 하나의 메서드가 선언된 인터페이스를 정의해서 람다식을 다루는 것은 기존의 자바 문법을 활용하여 표현할 수 있기 때문이다. 그래서 인터페이스를 통해 람다식을 다루기로 결정되었으며, 기존 함수의 정의대로 입력과 출력의 형태로 된 람다식을 다루는 인터페이스를 함수형 인터페이스
라고 부르기로 했다.
함수형 인터페이스임을 명시하고 싶을 때에는 @FunctionalInterface
를 붙일 수 있다. 생략할 수 있으며 어노테이션을 붙이고 두개 이상의 추상 메서드를 선언하면 컴파일 에러가 발생한다.
우테코 미션을 진행하면서 결과값을 지정할 수 없어 테스트하기 어려운 메서드를 함수형 인터페이스로 분리하여 람다식으로 값을 지정하고 테스트 하였다.
랜덤에 따른 결과에 따라 메서드의 동작이 달랐다. 랜덤 결과를 람다식을 이용해 예상 값을 지정하고 주 메서드의 동작을 테스트하였다.
public class CarTest {
@DisplayName("자동차가 이동하는 경우")
@Test
void car_drive_true() {
Car carDrive = new Car("drive");
Movable movable = () -> true; // 값 임의 지정
carDrive.drive(movable); // 테스트하려는 메서드
assertEquals(carDrive.getPosition(), 1);
}
@DisplayName("자동차가 대기하는 경우")
@Test
void car_drive_false() {
Car carDrive = new Car("drive");
Movable movable = () -> false; // 값 임의 지정
carDrive.drive(movable); // 테스트하려는 메서드
assertEquals(carDrive.getPosition(), 0);
}
}
객체를 생성하기 위해 사용자의 입력값을 받을 때, 적절한 입력값인지 검증해야 했다. 람다식을 이용하여 예상 입력값을 임의로 지정하여 그에 따른 테스트를 추가하였다.
class InputTest {
@Test
@DisplayName("구입 금액에 null 을 입력할 경우")
void payment_null() {
assertThatThrownBy(() -> {
Entering entering = () -> null; // 값 임의 지정
int payment = inputPayment(entering); // 테스트하려는 메서드
}).isInstanceOf(IllegalArgumentException.class);
}
@Test
@DisplayName("구입 금액에 빈값을 입력했을 경우")
void payment_empty() {
assertThatThrownBy(() -> {
Entering entering = () -> ""; // 값 임의 지정
int payment = inputPayment(entering); // 테스트하려는 메서드
}).isInstanceOf(IllegalArgumentException.class);
}
@Test
@DisplayName("구입 금액에 문자를 입력했을 경우")
void payment_not_number() {
assertThatThrownBy(() -> {
Entering entering = () -> "payment"; // 값 임의 지정
int payment = inputPayment(entering); // 테스트하려는 메서드
}).isInstanceOf(IllegalArgumentException.class);
}
}