Is there any way to access caller-scoped variables from an anonymous inner class in Java?
Here\'s the sample code to understand what I need:
public L
You need a 'container' to hold your value. You, however, do not have to create a container class. You may use classes in the java.util.concurrent.atomic
package. They provide an immutable wrapper for a value along with a set
and a get
method. You have AtomicInteger
, AtomicBoolean
, AtomicReference<V> (for your objects)
e.t.c
In the outer method:
final AtomicLong resultHolder = new AtomicLong();
In the anonymous inner class method
long result = getMyLongValue();
resultHolder.set(result);
Later in your outer method
return resultHolder.get();
Here's an example.
public Long getNumber() {
final AtomicLong resultHolder = new AtomicLong();
Session session = new Session();
session.doWork(new Work() {
public void execute() {
//Inside anonymous inner class
long result = getMyLongValue();
resultHolder.set(result);
}
});
return resultHolder.get(); //Returns the value of result
}
Java doesn't know that doWork is going to be synchronous and that the stack frame that result is in will still be there. You need to alter something that isn't in the stack.
I think this would work
final Long[] result = new Long[1];
and then
result[0] = st.getLong(4);
in execute()
. At the end, you need to return result[0];
Long is immutable. If you use a mutable class, holding a long value, you can change the value. For example:
public class Main {
public static void main( String[] args ) throws Exception {
Main a = new Main();
System.out.println( a.getNumber() );
}
public void doWork( Work work ) {
work.doWork();
}
public Long getNumber() {
final LongHolder result = new LongHolder();
doWork( new Work() {
public void doWork() {
result.value = 1L;
}
} );
return result.value;
}
private static class LongHolder {
public Long value;
}
private static abstract class Work {
public abstract void doWork();
}
}
As of Hibernate 4, the method Session#doReturningWork(ReturningWork<T> work) will return the return val from the inner method:
public Long getNumber(final String type, final String refNumber, final Long year) throws ServiceException {
try {
Session session = PersistenceHelper.getSession();
return session.doReturningWork(conn -> {
CallableStatement st = conn.prepareCall("{ CALL PACKAGE.procedure(?, ?, ?, ?) }");
st.setString(1, type);
st.setString(2, refNumber);
st.setLong(3, year);
st.registerOutParameter(4, OracleTypes.NUMBER);
st.execute();
return st.getLong(4);
});
} catch (Exception e) {
log.error(e);
}
return null;
}
(Cleaned up using a Java 8 lambda)
Using AtomicLong helped me in a very similar situation and the code looked clean.
// Create a new final AtomicLong variable with the initial value 0.
final AtomicLong YOUR_VARIABLE = new AtomicLong(0);
...
// set long value to the variable within inner class
YOUR_VARIABLE.set(LONG_VALUE);
...
// get the value even outside the inner class
YOUR_VARIABLE.get();
Anonymous classes/methods are not closures - this is exactly the difference.
The problem is that doWork()
could create a new thread to call execute()
and getNumber()
could return before the result is set - and even more problematically: where should execute()
write the result when the stack frame that contains the variable is gone? Languages with closures have to introduce a mechanism to keep such variables alive outside their original scope (or ensure that the closure is not executed in a separate thread).
A workaround:
Long[] result = new Long[1];
...
result[0] = st.getLong(4) ;
...
return result[0];