如何访问 Thread lambda 中的非最终变量?

How can I access a non-final variable in a Thread lambda?

我有一个自定义对象需要在 Thread lambda 中修改,因为我需要执行一个操作并为其分配一些值。

问题是当我在 Thread() 中声明变量时,它无法从封闭函数返回。然后,如果我试图使它成为一个全局变量并在线程内为其分配一些值,则无法完成,因为 lambda 只允许在其中使用 final 或有效的 final 变量。

什么是 workaround/solution?

 // Gives an undesired result 
 public class MeClass {
    public static Response response = new Response();

    // TODO: Make response specific to a method and not global

    public Response get(String endpoint) {
        new Thread(() -> {
            try {
                this.response = OffredUtil.makeGetRequest(endpoint);
            } catch (Exception e) {
                this.response.isException = true;
                Log.d(TAG, e.getMessage());
            }
        }).start();
        return this.response;
    }
    // Another method with similar function accessing response

}

所以我想在方法本身内部声明 response,但由于只有 final 变量可用,所以我不能这样做。

// Gives an error
public Response get(String endpoint) {
        Response response = new Response();
        new Thread(() -> {
            try {
                response = OffredUtil.makeGetRequest(endpoint);
            } catch (Exception e) {
                this.response.isException = true;
                Log.d(TAG, e.getMessage());
            }
        }).start();
        return response;

假设这是允许的?您希望它达到什么效果 return?

// Warning! This is an example of what *NOT* to do.
//
public Response get(String endpoint) {
    Response response = new Response();
    new Thread(() -> {
        response = OffredUtil.makeGetRequest(endpoint);
    }).start();
    return response;
}

没有理由认为 response = OffredUtil.makeGetRequest(endpoint); 语句会在 return response; 语句之前执行。事实上,它可能 不会 直到一段时间后才被执行。

你真正想要的是;

  • 你的 get(endpoint) 方法到 return 一个 可变 对象,并且
  • 调用者等待直到新值被其他线程存储到可变对象中的方法。

Java 标准库为这种可变对象定义了一个接口:它叫做 java.util.concurrent.FutureFuture 有一个 get() 方法,如有必要,该方法将等待,直到其他线程通过给它一个值 完成 Future ,然后 get() 将 return 值。

最简单的使用方法是通过 CompletableFuture class:

import java.util.concurrent.Future;
import java.util.concurrent.CompletableFuture;
...
public Future<Response> get(String endpoint) {
    return CompletableFuture.supplyAsync(() -> {
       return OffredUtil.makeGetRequest(endpoint);
    });
}

调用此 get(endpoint) 方法会将任务提交给 built-in 线程池,该线程池将执行给定的 lambda 表达式,然后它将 return 一个 Future 将由任务完成。

如果 lambda 产生一个值,那么它将成为 Future 的值。如果 lambda 抛出一个异常,那么就会被捕获,异常对象会被存储在 Future

get(endpoint) 的调用者可以这样做:

...
Future<Response> fr = myClassInstance.get(endpoint);
doSomethingElseConcurrentlyWithThe_makeGetRequest_call(...);
try {
    Response r = fr.get();
    ...
} catch (Exception e) {
    o.response.isException = true;
    Log.d(TAG, e.getMessage());
}