Is there a best practice for using Guava's ListenableFuture in parameters in API calls? Compare these two versions of an interface that internally does a compare-and-set (I'm using Guava's ListenableFuture):
interface MyDao {
ListenableFuture<Data> getData(String id);
ListenableFuture<Boolean> updateData(ListenableFuture<Data> data, int newFieldValue);
}
interface MyDao {
ListenableFuture<Data> getData(String id);
ListenableFuture<Boolean> updateData(Data data, int newFieldValue);
}
The first looks a bit messy, and the implementation will likely involve a Futures.transform of its own, but you could also argue that it's an asynchronous API, so taking futures as parameters might make sense. Where it gets nice is that it tidies up the business logic when using the interface so it reads like synchronous code:
ListenableFuture<Data> data = dao.getData("123");
ListenableFuture<Boolean> success = dao.updateData(data, 10);
Compare that to
ListenableFuture<Data> data = dao.getData("123");
ListenableFuture<Boolean> success = Futures.transform(data, (Data d) -> dao.updateData(d, 10));
And that's with a lambda. It's hideous in Java 7.
The obvious drawback is the interface looks inconsistent. Why is data a future, but newFieldValue isn't? Should id be a future, too? Along those lines, it makes a trivial call incredibly complicated—almost like Java wrapper objects before autoboxing.
It also doesn't do anything to clean up hideous stack traces when futures fail; they still cascade through every transform.
modifyDatamethod with parameters ofString id, Function[Data => Data]