6

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.

  • Rather than expecting the user to compose the two futures why not have a single blind modifyData method with parameters of String id, Function[Data => Data] – Daenyth Jan 22 '16 at 18:49
1

I think having a ListenableFuture in an API signature method doesn't make it inconsistent.

It's only there to declare explicity to the user an asynchronous computation while hiding the boilerplate thread logic. Like an Optional would declare explicity to the user a possibility of missing data while hiding the boilerplate "if not null" logic.

These 2 links explain a bit more my thoughts: Monadic Java and Use of optional

However, if you think any user of your API potentially want to call updateData but have only an instance of Data you could consider adding the corresponding method to your API.

public interface MyDao {
    ListenableFuture<Boolean> updateData(ListenableFuture<Data> data, int newFieldValue);
    ListenableFuture<Data> getData(String id);
    default ListenableFuture<Boolean> updateData(Data data, int newFieldValue) {
        //TODO: wrap data in a ListenableFuture and call updateData
    }
}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.