从匿名内部类设置外部variables

有没有办法从Java中的匿名内部类访问调用者范围的variables?

以下是示例代码,了解我需要什么:

public Long getNumber(final String type, final String refNumber, final Long year) throws ServiceException { Long result = null; try { Session session = PersistenceHelper.getSession(); session.doWork(new Work() { public void execute(Connection conn) throws SQLException { 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(); result = st.getLong(4) ; } }); } catch (Exception e) { log.error(e); } return result; } 

代码在DAO服务类中。 显然,它不会编译,因为它要求result是最终的,如果是 – 它不会编译,因为我尝试修改最终的variables。 我被绑定到JDK5。 除了完全删除doWork() ,是否有一种方法可以在doWork()设置结果值?

Java不知道doWork将会是同步的,并且结果所在的堆栈帧将仍然存在。 你需要改变不在堆栈中的东西。

我认为这将工作

  final Long[] result = new Long[1]; 

接着

  result[0] = st.getLong(4); 

execute() 。 最后,你需要return result[0];

这种情况在Java中出现了很多,处理它的最简单的方法是使用一个简单的值容器类。 这与arrays方法是一样的,但它更清洁IMO。

 public class ValContainer<T> { private T val; public ValContainer() { } public ValContainer(T v) { this.val = v; } public T getVal() { return val; } public void setVal(T val) { this.val = val; } } 

长是不变的。 如果你使用一个可变类,保存一个很长的值,你可以改变它的值。 例如:

 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(); } } 

如果包含的类是MyClass – >

 MyClass.this.variable = value; 

不记得这是否会与一个私有variables(我认为它会工作)。

只适用于类的属性(类variables)。 不适用于方法局部variables。 在JSE 7中可能会有closures做这样的事情。

标准的解决scheme是返回一个值。 例如,请参阅java.security.AccessController.doPrivileged

所以代码看起来像这样:

 public Long getNumber( final String type, final String refNumber, final Long year ) throws ServiceException { try { Session session = PersistenceHelper.getSession(); return session.doWork(new Work<Long>() { public Long execute(Connection conn) throws SQLException { CallableStatement st = conn.prepareCall("{ CALL PACKAGE.procedure(?, ?, ?, ?) }"); try { st.setString(1, type); st.setString(2, refNumber); st.setLong(3, year); st.registerOutParameter(4, OracleTypes.NUMBER); st.execute(); return st.getLong(4); } finally { st.close(); } } }); } catch (Exception e) { throw ServiceException(e); } } 

(还修复了潜在的资源泄漏,并返回null的任何错误。)

更新:显然, Work来自第三方库,不能更改。 所以我build议不要使用它,至less隔离你的应用程序,以免直接使用它。 就像是:

 public interface WithConnection<T> { T execute(Connection connnection) throws SQLException; } public class SessionWrapper { private final Session session; public SessionWrapper(Session session) { session = nonnull(session); } public <T> T withConnection(final WithConnection<T> task) throws Service Exception { nonnull(task); return new Work() { T result; { session.doWork(this); } public void execute(Connection connection) throws SQLException { result = task.execute(connection); } }.result; } } 

从Hibernate 4开始, Session#doReturningWork(ReturningWork<T> work)方法将从内部方法返回返回值val:

 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; } 

(使用Java 8 lambda清理)

匿名类/方法不是closures – 这正是差异。

问题是, doWork()可能会创build一个新的线程来调用execute()getNumber()可能会返回之前的结果设置 – 甚至更有问题:应在哪里execute()写结果时,包含variables离开了? 有闭包的语言必须引入一个机制来保持这些variables超出其原始范围(或确保闭包不在一个单独的线程中执行)。

解决方法:

 Long[] result = new Long[1]; ... result[0] = st.getLong(4) ; ... return result[0];