英文:
How can I access a non-final variable in a Thread lambda?
问题
我有一个自定义对象,需要在一个Thread lambda内部进行修改,因为我需要执行一个操作并为其分配一些值。
问题是,当我在Thread()中声明变量时,无法从封闭函数中返回它。然后,如果我尝试将其变成全局变量并在Thread内部赋值,那是不可能的,因为lambda只允许在其中使用final或有效final的变量。
有什么解决方法/解决方案可以解决这个问题?
// 给出了不期望的结果
public class MeClass {
public static Response response = new Response();
// TODO: 使response只针对一个方法具体化,而不是全局的
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;
}
// 具有类似功能且访问response的另一个方法
}
所以我想在方法内部声明response
,但由于只有final变量可用,我无法这样做。
// 给出了一个错误
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;
}
英文:
I have a custom object that I need to modify inside a Thread lambda, as I need to perform an operation and assign some value to it.
The problem is that when I declare the variable in the Thread(), it cannot be returned from the enclosing function. Then, if I try to make it a global variable and assign some value to it inside the Thread, it can't be done because lambdas only allow final or effectively final variables inside them.
What can be a workaround/solution for this?
// 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
}
So I want to declare the response
inside the method itself, but I can't do it due to only final variables being available.
// 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;
答案1
得分: 1
假设这是被允许的?你会期望它返回什么?
// 警告!这是一个示范,不应该这么做。
//
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)
方法返回一个可变对象,以及 - 让调用者能够等待,直到其他线程将新值存储到可变对象中。
Java 标准库定义了一个适用于这种可变对象的接口:它叫做 java.util.concurrent.Future
。Future
有一个 get()
方法,如果必要的话,它会等待,直到某个其他线程通过给它一个值来完成该 Future
,然后 get()
将返回这个值。
最简单的使用方式是通过 CompletableFuture
类:
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)
方法的调用将会向内置线程池提交一个任务,该任务将执行给定的 lambda 表达式,然后返回一个将被该任务完成的 Future
。
如果 lambda 产生一个值,那么它将成为 Future
的值。如果 lambda 抛出异常,那么它会被捕获,并且异常对象将被存储在 Future
中。
调用 get(endpoint)
的调用者可以这样做:
...
Future<Response> fr = myClassInstance.get(endpoint);
与 makeGetRequest 调用同时进行其他操作(...);
try {
Response r = fr.get();
...
} catch (Exception e) {
o.response.isException = true;
Log.d(TAG, e.getMessage());
}
英文:
Suppose this was allowed? What would you expect it to 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;
}
There's no reason to think that response = OffredUtil.makeGetRequest(endpoint);
statement will be executed until before the return response;
statement. In fact, it probably will not be executed until some time later.
What you really want is;
- for your
get(endpoint)
method to return a mutable object, and - a way for a caller to wait until a new value has been stored into the mutable object by some other thread.
The Java standard library defines an interface for just that kind of mutable object: It's called java.util.concurrent.Future
. A Future
has a get()
method that will wait, if necessary, until some other thread has completed the Future by giving it a value, and then the get()
will return the value.
The simplest way to use it is through the 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);
});
}
A call to this get(endpoint)
method will submit a task to a built-in thread pool that will execute the given lambda expression, and then it will return a Future
that will be completed by the task.
If the lambda produces a value, then that will become the value of the Future
. If the lambda throws an exception, then that will be caught and, and the exception object will be stored in the Future
The caller of get(endpoint)
can do this:
...
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());
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论