Supplier<T> interface
함수형 인터페이스를 공부하면서 Java가 기본으로 제공하는 인터페이스 중 Supplier를 알게 되었습니다.
이 인터페이스는 구현체에서 반환할 타입을 지정하고
추상 메서드인 get() 메서드를 재정의하여 지정된 타입의 값을 반환받는 아주 간단한 인터페이스입니다.
저는 이 인터페이스의 존재가 "굳이..?" 라는 생각이 들어서 찾아보니 Lazy Evaluation을 위해서 존재한다는 것을 찾게 되었습니다.
Lazy Evaluation
이것은 불필요한 연산을 피하기 위해서 연산을 지연시키는 것을 뜻합니다.
뜻만 알아서는 존재의 이유를 잘 모르겠어서 예제를 통해 이해해 보겠습니다.
추상 메서드인 get() 메서드를 재정의하여 현재 시간을 time 변수에 저장하고 3초를 멈추었다가 time 변수와 함께 조합된 String 값을 반환해 봅시다.
또한, 항상 실행되는 것이 아니라 조건에 맞을 경우에만 실행이 되도록 해서 불필요한 연산을 더하겠습니다.
public class TestSupplier {
public static void main(String[] args) {
long start = System.currentTimeMillis();
printValidNumber(0, getTime());
printValidNumber(-1, getTime());
printValidNumber(-2, getTime());
System.out.println("It took " + ((System.currentTimeMillis() - start) / 1000) + " seconds");
}
public static String getTime() {
long time = System.currentTimeMillis();
try {
TimeUnit.SECONDS.sleep(3);
} catch(InterruptedException e) {
e.printStackTrace();
}
return "In milliseconds, 3 seconds ago is "+ time +".";
}
public static void printValidNumber(int number, String value) {
if(number >= 0) {
System.out.println(value);
} else {
System.out.println("Invalid");
}
}
}
이렇게 구현하고 실행하면 결과는

이렇게 3초가 총 3번 반복되어 9초가 걸렸다고 나오게 됩니다.
이는 당연하게도 getTime() 메서드가 실행되어 반환된 문자열을 printValidNumber() 메서드의 인자값으로 넣어주기 때문입니다.
여기서 우리는 불필요한 연산들이 실행되는 것을 발견할 수 있습니다.
Lazy Evaluation의 뜻을 다시 보면 "불필요한 연산을 피하기 위해서 지연시키는 것" 입니다.
즉, 내가 원하는 상황이 아니어도 getTime() 메서드가 실행되어 발생하는 불필요한 연산을 피하기 위해서 getTime() 메서드의 실행을 지연시킬 필요가 있습니다.
이때, 우리는 Supplier 인터페이스를 사용하여 문제를 해결할 수 있습니다.
Supplier 인터페이스의 get() 추상 메서드를 getTime() 메서드가 실행되도록 재정의하도록 코드를 바꾸어 보겠습니다.
public class TestSupplier {
public static void main(String[] args) {
long start = System.currentTimeMillis();
printValidNumber(0, () -> getTime());
printValidNumber(-1, () -> getTime());
printValidNumber(-2, () -> getTime());
System.out.println("It took " + ((System.currentTimeMillis() - start) / 1000) + " seconds");
}
public static String getTime() {
long time = System.currentTimeMillis();
try {
TimeUnit.SECONDS.sleep(3);
} catch(InterruptedException e) {
e.printStackTrace();
}
return "In milliseconds, 3 seconds ago is "+ time +".";
}
public static void printValidNumber(int number, Supplier<String> value) {
if(number >= 0) {
System.out.println(value.get());
} else {
System.out.println("Invalid");
}
}
}
이렇게 실행해 보면

3초의 시간이 걸렸다는 것을 확인할 수 있습니다.
String대신 Supplier를 printValidNumber 메서드의 인자값으로 받으면서,
getTime() 메서드를 조건에 맞을 경우에만 get() 메서드를 통해서 실행할 수 있습니다.
결론
이처럼 불필요한 연산이 사용될 수 있는 부분을 Supplier 인터페이스의 get() 메서드에 재정의 하여 사용한다면, 그 부분의 실행을 지연시켜 불필요한 연산을 줄일 수 있다는 것을 확인할 수 있었습니다.