Lazy Evaluation는 불필요한 연산을 피하기 위해 연산을 지연시켜 놓았다가 필요할 때 연산하는 방법이다.
아들: 엄마 나 졸린데, 잘려면 침대랑 베개가 필요해.
엄마: 2개의 방중에 어떤방에 침대랑 베개가 있는지 모르겠네? 엄마는 바쁘니까 아들이 한번 찾아봐
아들: 알겠어.
아들은 잠을 자기위해 2개의 방 중에 침대와 베개 둘다 있는 방을 찾아야 잠을 잘 수 있다.
static String sleep() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "sleep";
}
static void print(boolean bed , boolean pillow, String sleep) {
if(bed && pillow) {
System.out.println(sleep);
} else {
System.out.println("don't sleep");
}
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
print(true, true, sleep()); // room1
print(true, false, sleep()); // room2
System.out.println((System.currentTimeMillis() - startTime) / 1000 + "초");
}
/*
출력
sleep
don't sleep
2초
*/
위 코드를 보면 room2
는 bed: true
지만 pillow: false
라는 사실을 우리는 알고있다. 하지만 그거와 별개로 sleep()는 항상 실행하기 때문에 2초라는 시간이 걸린다.
아이는 잠을 자놓고 안 잔척한다.
메모리와 시간을 낭비한다.
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
Java 8에서는 다양한 Funtional 함수들이 추가되었다. 그중에 Supplier
라는 함수가 있는데,
보이는 거처럼 파라미터를 하나도 받지 않고 T라는 객체를 생성하는 함수형 인터페이스다.
Supplier는 Lazy Evaluation
을 지원한다.
static String sleep() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "sleep";
}
static void print(boolean bed, boolean pillow, Supplier<String> sleep) {
if(bed && pillow) {
System.out.println(sleep.get());
} else {
System.out.println("don't sleep");
}
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
print(true,true, () -> sleep()); // room1
print(true,false, () -> sleep()); // room2
System.out.println((System.currentTimeMillis() - startTime) / 1000 + "초");
}
/*
출력
sleep
don't sleep
1초
*/
아이는 불필요한 연산(room2)
에서는 잠을 자지 않는다.
Java 7 이전에도 Lazy Evaluation을 구현 할 수는 있었다. 하지만 문제점이 존재했다.
static boolean returnTrue() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return true;
}
static boolean returnFalse() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return false;
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
boolean bed = returnTrue();
boolean pillow = returnFalse();
if(pillow && bed) {
System.out.println("잘 수 있다.");
} else {
System.out.println("잘 수 없다.");
}
System.out.println((System.currentTimeMillis() - startTime) / 1000 + "초");
}
/*
출력
잘 수 없다.
2초
*/
위의 코드를 개선해보자.
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
if(returnFalse() && returnTrue()) {
System.out.println("잘 수 있다.");
} else {
System.out.println("잘 수 없다.");
}
System.out.println((System.currentTimeMillis() - startTime) / 1000 + "초");
}
/*
출력
잘 수 없다.
1초
*/
위 코드처럼 개선할 수 있다.
returnFalae()에서 false를 리턴하므로 뒤는 볼거도 없이 else문을 출력한다. 즉 Java 자체에서 지연 연산을 지원한다.
하지만 함수의 리턴 값을 변수에 초기화 할 수 없다. 이 문제점을 해결하기 위해 Java 8에 Supplier 함수형 인터페이스가 도입됐다.
supplier.get()
orElse(함수A()) orElse는 함수A()를 무조건 실행하지만,
orElseGet(함수A()) orElseGet는 함수A()를 lazy 연산 하기 때문에 연산이 필요한 경우에만 동작한다.