overload patterns/anti-patterns (original) (raw)

Doug Lea dl at cs.oswego.edu
Sat Dec 29 05:06:17 PST 2012


As Remy pointed out when I posted first sketch of CompletableFuture, overloading methods on the basis of function type will often force people to add explicit casts on lambdas. Which is not something we'd like to require, especially because the cases are fragile and depend on bits of punctuation. For example "x -> foo()" can be either Block or Function, but "x -> { foo(); }" can only be Block.

I had since reworked CompletableFuture to avoid the most common ambiguous shapes in overloads. But a couple remain, and people are already starting to encounter them. My current sense is that, no matter how much lambda-overload matching is tweaked, this will remain a common API design gotcha, and the best advice is to never overload solely on function type.

So I'm about to rename some CompletableFuture methods: CF.then(fn) => CF.thenApply(fn) CF.then(runnable) => CF.thenRun(runnable) in turn allowing re-introduction of the doubly-problematic Block form: CF.thenAccept(block). Similarly for others, including CF.async(runnable) => CF.runAsync(runnable)

I figure that if we are stuck with different method names for different functional forms, then I might as well exploit it here to use in overload stems. Supplier doesn't fit well in this scheme though. The method name "get()" is a terrible overload-stem, so instead: CF.async(Supplier) => CF.supplyAsync(Supplier)

(BTW Supplier is lambda-confusable with Runnable).

But this also is an argument for changing the method name for Supplier to be "supply".

-Doug



More information about the lambda-libs-spec-experts mailing list