Somasundaram Sekar
Somasundaram Sekar

Reputation: 5524

Closing over immutable variable and accumulate values across multiple iterations as a lambda expression - Java 8

WebTarget in Jersey client is implemented as a immutable object, and any operations to change the state returns a new WebTarget. To add query params to it, which is coming in as a Map<> the following code was written.

public WebTarget webTarget(String path, Map<String, String> queryMap) {

    WebTarget webTarget = client.target(this.address.getUrl()).path(path);
    if (queryMap != null)
        queryMap.entrySet().forEach(e -> webTarget.queryParam(e.getKey(), e.getValue()));
    return webTarget;
 }

The problems here is every call to .queryParam() returns a new WebTarget and I'm stuck at how to accumulate as the variables used inside a lambda expression has to be either final or implicitly final, without any reassignments.

EDIT: Reduce may not be an option in this case, as WebTarget lack the mechanism for reduction, that I cannot get the queryParam from one webtarget and set it into another. if the WebTarget api had better support for accumulation, it could have been used.

Used Jool to try and leverage foldLeft which is lacking in native Java 8 API, but still hit rock bottom as WebTarget api lacked the support for it.

EDIT2: foldLeft is the way the go as suggested in the answer below, did write a small blog on this

Upvotes: 2

Views: 421

Answers (2)

matt
matt

Reputation: 12347

You can use the ol' array trick, which is not good for anything more than a proof of concept.

WebTarget[] webTarget = {client.target(this.address.getUrl()).path(path)};
if (queryMap != null){
    queryMap.forEach((k, v)->{
        webTarget[0] =  webTarget[0].queryParam(k, v);
    });
}
return webTarget[0];

You could improve it by using an AtomicReference.

AtomicReference<WebTarget> webTarget = new AtomicReference<>(client.target(this.address.getUrl()).path(path));
if (queryMap != null){
    queryMap.forEach((k, v)->{
        webTarget.updateAndGet(w->w.queryParam(k, v));
    });
}
return webTarget.get();

Upvotes: 1

Sergei Rybalkin
Sergei Rybalkin

Reputation: 3453

If you want functional approach you need foldLeft(right) or reduce.

foldLeft is implemented in some libraries, for example Functionaljava and streamEx.

Functionaljava:

<B> B foldLeft(final F<B, F<A, B>> bff, final B z)

WebTarget wt = wtCollection.foldLeft(x -> (y -> x.queryParam(...)), new WebTarget());

StreamEx:

<U> U foldLeft(U seed, BiFunction<U, ? super T, U> accumulator) 

UPD stream-reduce

queryMap.entrySet().stream()
    .reduce(new WebTarget(), (x, y) -> { 
        x.queryParam(y.getKey(), y.getValue()); 
    });

Upvotes: 2

Related Questions