Evaluators { public static <T, R> Function<T, R> memoizeLast(Function<T, R> function) { AtomicReference<Pair<T, Either<R, Throwable>>> lastRef = new AtomicReference<>(); return argument -> { Pair<T, Either<R, Throwable>> last = lastRef.get(); if (last != null && Objects.equals(argument, last.getLeft())) { if (last.getRight().hasValue()) { return last.getRight().getValue(); } Throwable error = last.getRight().getError(); if (error instanceof RuntimeException) { throw (RuntimeException) error; } if (error instanceof Error) { throw (Error) error; } throw new IllegalStateException(error); } try { R result = function.apply(argument); lastRef.set(Pair.of(argument, Either.ofValue(result))); return result; } catch (Throwable e) { lastRef.set(Pair.of(argument, Either.ofError(e))); throw e; } }; } private  Evaluators(); static void consumeNothing(T value); static void doNothing(); static void acceptNotNull(T value, Consumer<T> consumer); static void acceptIfTrue(T value, Consumer<Boolean> consumer); static R applyNotNull(T value, Function<T, R> transformer); static T getOrDefault(T value, T defaultValue); static T getFirstNotNull(T... values); static void times(int count, Runnable task); static void times(int count, Consumer<Integer> task); static List<T> evaluateTimes(int count, Function<Integer, T> transformer); @SafeVarargs static Optional<T> firstPresent(Supplier<Optional<T>>... optionalSuppliers); static Function<T, R> memoizeLast(Function<T, R> function); static Function<T, R> memoizeLast(BiFunction<T, Optional<R>, R> computation);  }
@Test public void testMemoizeLast() { Function<String, String> evaluator = Evaluators.memoizeLast(s -> { counter++; return ("" + s).toUpperCase(); }); assertThat(evaluator.apply("a")).isEqualTo("A"); assertThat(evaluator.apply("a")).isEqualTo("A"); assertThat(counter).isEqualTo(1); assertThat(evaluator.apply(null)).isEqualTo("NULL"); assertThat(evaluator.apply(null)).isEqualTo("NULL"); assertThat(counter).isEqualTo(2); assertThat(evaluator.apply("b")).isEqualTo("B"); assertThat(evaluator.apply("b")).isEqualTo("B"); assertThat(counter).isEqualTo(3); }
@Test public void testMemoizeLastError() { Function<String, String> evaluator = Evaluators.memoizeLast(s -> { counter++; if (s.equals("error")) { throw SIMULATED_ERROR; } return s.toUpperCase(); }); applyWithErrorResult(evaluator); applyWithErrorResult(evaluator); assertThat(counter).isEqualTo(1); assertThat(evaluator.apply("a")).isEqualTo("A"); assertThat(counter).isEqualTo(2); }
@Test public void testMemoizeLastWithBiFunction() { AtomicInteger counter = new AtomicInteger(0); Function<String, Integer> timesCalled = Evaluators.memoizeLast((input, lastResult) -> counter.incrementAndGet()); for (int i = 0; i < 5; i++) { assertThat(timesCalled.apply("foo")).isEqualTo(1); } for (int i = 0; i < 5; i++) { assertThat(timesCalled.apply("bar")).isEqualTo(2); } assertThat(timesCalled.apply("foo")).isEqualTo(3); assertThat(timesCalled.apply("bar")).isEqualTo(4); }