L'accesso variabile esterna da interno Runnable anonimo
-
21-09-2019 - |
Domanda
Il seguente codice di esempio (SSCCE) lamenta che variabile locale a deve essere definitiva.
public class Foo {
final List<A> list = new ArrayList() {{ add(new A()); }};
void foo() {
A a;
Thread t = new Thread(new Runnable() {
public void run() {
a = list.get(0); // not good !
}
});
t.start();
t.join(0);
System.out.println(a);
}
class A {}
}
Per far funzionare le cose modificare il codice a quello
public class Foo {
final List<A> list = new ArrayList() {{ add(new A()); }};
void foo() {
// A a;
final ObjectRef x = new ObjectRef();
Thread t = new Thread(new Runnable() {
public void run() {
// a = list.get(0);
x.set(list.get(0));
}
});
t.start();
t.join(0);
// System.out.println(a);
System.out.println(x.get());
}
class A {}
class ObjectRef<T> {
T x;
public ObjectRef() {}
public ObjectRef(T x) { this.x = x; }
public void set(T x) { this.x = x; }
public T get() { return x; }
}
}
Le mie domande:
- C'è qualcosa di sbagliato in questo?
- La classe ObjectRef esiste come classe standard in JSE?
- Qual è il modo giusto?
Soluzione
Avete considerato utilizzando Callable
invece? Callable
può essere utilizzato quando si produce un risultato, che sembra essere il vostro caso.
final List<A> list = new ArrayList() {{ add(new A()); }};
void foo() {
Callable<A> call = new Callable<A> {
A call() throws Exception
{
// do something with the list
return list.get(0);
}
}
ExecutorService executor = new ScheduledThreadPoolExecutor(1);
Future<A> future = executor.submit(call);
System.out.println( future.get() );
}
Altri suggerimenti
modo Destra sta usando FutureTask ed esigibili
FutureTask task = new FutureTask(new Callable<A>() {
public A call() {
return list.get(0);
}
});
Executor ex = Executors.newFixedThreadPool(1);
ex.execute(task);
// do something else, code is executing in different thread
A a = task.get(); //get result of execution, will wait if it's not finished yet
ex.shutdown();
Sono d'accordo che si dovrebbe andare con Callable e FutureTask.
Ma non può essere necessario utilizzare un esecutore: se non si ha intenzione di condividere questa esecutore con altro codice, le tre linee necessarie per crearlo, presenterà il compito, e poi spegnerlo di nuovo, sembrano troppo prolisso. Si potrebbe utilizzare un filo.
FutureTask<A> task = new FutureTask(new Callable<A>() {
public A call() {
return list.get(0);
}
});
new Thread(task).start();
A result = task.get();