简体   繁体   中英

Lambda notation - Trying to refactor but getting issues

I have a number of methods that are mapped to server requests. The requests use promises (from Play Framework) to return responses some time in the future. Here is an example of one of my actions:

public static Promise<Result> someAction() {
    MyBean data = new MyBean(getData(request().body()));
    Promise<MyReq> promise = Promise.promise(() -> new MyReq(data));
    return promise.map((MyReq req) -> ok(req.getResponse()));
}

Note that MyReq extends MyAbstractReq<T> .

What I was hoping to do was to be able to take all of the common code out of all the action methods and bundle it into a single common handleRequest method. However I struggle to understand Lambda Notation and cannot modify the code to suit my purposes.

I understand that () -> new MyReq(data) basically means (arg1, arg2, ...) -> { body } which can be written like (I think):

MyBean data = new MyBean(getData(request().body()));

// Anonymous Functional Interface
MyFunction f = new MyFunction() {      
    @Override
    public void getReq(){
        return new MyReq(data);
    }
};

Or something like that.

But what I am trying to achieve is this something like this (doesn't compile but I hope it gives enough of an idea what I'm trying to achieve):

public static Promise<Result> handleRequest(MyAbstractReq<?> req) {
    Promise<MyAbstractReq<?>> promise = Promise.promise(() -> req);
    return promise.map(() -> ok(req.getResponse()));
}

And each action looks like this:

public static Promise<Result> someAction() {
    MyBean data = new MyBean(getData(request().body()));
    return handleRequest(new MyReq(data));
}

Can someone please point out what it is that I am missing with regards to Lambda notation and explain how I can achieve what I am trying to do.

Update

The error I get is

Lambda expression's signature does not match the signature of the functional interface method apply(? super MyAbstractReq<?>)

I was guessing that that was because the syntax was incorrect, it was just my first naive attempt to refactor it.

The req.getResponse() method is not really relevant as it just generates a Json object (ObjectNode - Jackson faster xml) which can be consumed by the ok() method to create a Result

Update 2

With regards to using handleRequest(MyAbstractReq<T> req) I initially actually had this but the error was basically the same.

Lambda expression's signature does not match the signature of the functional interface method apply(? super MyAbstractReq<T>)

Update 3

I beg your pardon. The error is on the line return promise.map(() -> ok(req.getResponse()));

Update 4

I probably should have mentioned that I know how to solve the issue by changing the handleRequest() method to:

public static Promise<Result> handleRequest(MyAbstractReq<?> req) {
    Promise<MyAbstractReq<?>> promise = Promise.promise(() -> req);
    return promise.map((MyAbstractReq<?> notUsed) -> ok(req.getResponse()));
}

But it feels wrong as the line return promise.map((MyAbstractReq<?> notUsed) -> ok(req.getResponse())); takes an argument of MyAbstractReq<?> which is never used.

I don't understand enough about the notation of Lambda expressions to tell if this is not the best way to achieve what I want (I didn't mention it as I wanted to see what people's suggestions would be without me leading them to an answer).

In your initial code

public static Promise<Result> someAction() {
    MyBean data = new MyBean(getData(request().body()));
    Promise<MyReq> promise = Promise.promise(() -> new MyReq(data));
    return promise.map((MyReq req) -> ok(req.getResponse()));
}

everything works as intended as map gets a function which specifies how to process the argument req . But in your refactored code you are suddenly attempting to access a variable from the outer scope instead of keeping the logic:

public static Promise<Result> handleRequest(MyAbstractReq<?> req) {
    Promise<MyAbstractReq<?>> promise = Promise.promise(() -> req);
    // the following function refers to handleRequest’s parameter instead of its argument
    return promise.map((MyAbstractReq<?> notUsed) -> ok(req.getResponse()));
}

This works in this special case but is indeed as wrong as your feeling tells you.

Just stay with the original logic here:

public static Promise<Result> handleRequest(MyAbstractReq<?> req) {
    Promise<MyAbstractReq<?>> promise = Promise.promise(() -> req);
    // arg is unrelated to req here though both variables happen to have to same contents
    return promise.map(arg -> ok(arg.getResponse()));
}

but it is still a bit strange as the promise is constructed around an already existing object instead of constructing it. Maybe you want

public static Promise<Result> handleRequest(Function0<MyAbstractReq<?>> supplier) {
    Promise<MyAbstractReq<?>> promise = Promise.promise(supplier);
    return promise.map(arg -> ok(arg.getResponse()));
}

so you can call it like

handleRequest( ()-> new MyReq(new MyBean(getData(request().body()))) );

as then the Promise will really cover the entire operation.

Now after your update 2, you've come up with following method:

public static Promise<Result> handleRequest(MyAbstractReq<?> req) {=
    Promise<MyAbstractReq<?>> promise = Promise.promise(() -> req);
    return promise.map((MyAbstractReq<?> notUsed) -> ok(req.getResponse()));
}

Before proceeding further, let me explain what map() does. On a given Iterable , when applied, map() will simply transform each element to something else (which might be something extracted out of the element). For this, we pass a Function to the map() method, which takes an input and returns an output.

In this case, (MyAbstractReq<?> notUsed) -> ok(req.getResponse()) is that function. You take notUsed as argument (which is one element from promise , and call ok() method on response of that object.

But you see, you're not using the argument you passed, but a different object (the one you passed as argument to handleRequest() method. That is wrong. You should use notUsed.getResponse() there.

Also, you don't need to give type of notUsed there. Java can infer the type. So, change your method to:

public static Promise<Result> handleRequest(MyAbstractReq<?> req) {=
    Promise<MyAbstractReq<?>> promise = Promise.promise(() -> req);
    return promise.map(promReq -> ok(promReq.getResponse()));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM