Java 从匿名内部类设置外部变量
在Java中,有没有办法从匿名内部类访问调用方范围的变量 以下是我需要的示例代码:Java 从匿名内部类设置外部变量,java,anonymous-class,Java,Anonymous Class,在Java中,有没有办法从匿名内部类访问调用方范围的变量 以下是我需要的示例代码: public Long getNumber(final String type, final String refNumber, final Long year) throws ServiceException { Long result = null; try { Session session = PersistenceHelper.getSession();
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
是final,如果是的话--它没有编译,因为我试图修改final变量。我绑定到JDK5。如果包含的类是MyClass-->
不记得这是否适用于私有变量(我认为它会起作用)
仅适用于类的属性(类变量)。不适用于方法局部变量。在JSE 7中,可能会有闭包来做这类事情。Java不知道doWork将是同步的,并且结果所在的堆栈帧仍然存在。您需要更改不在堆栈中的内容 我想这会管用的
final Long[] result = new Long[1];
然后
result[0] = st.getLong(4);
在
execute()
中。最后需要返回结果[0]代码>Long是不可变的。如果您使用一个可变类,持有一个长值,那么您可以更改该值。例如:
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();
}
}
匿名类/方法不是闭包-这正是区别所在
问题是doWork()
可能会创建一个新线程来调用execute()
,而getNumber()
可能会在设置结果之前返回,甚至更麻烦的是:当包含变量的堆栈帧消失时,execute()
应该在哪里写入结果?带有闭包的语言必须引入一种机制,使这些变量在其原始范围之外保持活动状态(或者确保闭包不在单独的线程中执行)
解决办法:
Long[] result = new Long[1];
...
result[0] = st.getLong(4) ;
...
return result[0];
对此的标准解决方案是返回一个值。例如,请参见yeoldejava.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);
}
}
这种情况在Java中经常出现,最干净的处理方法是使用一个简单的值容器类。它与数组方法的类型相同,但在我看来更干净
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;
}
}
公共类容器{
私人旅行社;
公共容器(){
}
公共集装箱(T v){
这个。val=v;
}
公共T getVal(){
返回val;
}
公共无效设置值(T值){
this.val=val;
}
}
从Hibernate 4开始,该方法将从内部方法返回返回值:
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;
}
final AtomicLong resultHolder = new AtomicLong();
(使用Java 8 lambda清理)您需要一个“容器”来保存您的价值。但是,您不必创建容器类。您可以使用java.util.concurrent.atomic
包中的类。它们为值提供了一个不可变的包装器,以及一个set
和get
方法。您有AtomicInteger
,AtomicBoolean
,AtomicReference(用于您的对象)
e.t.c
在外部方法中:
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;
}
final AtomicLong resultHolder = new AtomicLong();
在匿名内部类方法中
long result = getMyLongValue();
resultHolder.set(result);
稍后在您的外部方法中
return resultHolder.get();
这里有一个例子
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
}
在一个非常类似的情况下,使用帮助我,代码看起来很干净
// 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();
Java7不会有闭包。即使在Java 8中引入了它们,它们也可能无法写入局部变量(即“访问的局部变量必须是最终的”规则可能仍然有效)。评论有点晚了,但在Java 8中似乎确实有效。虽然这可以起作用,但这是一个有点丑陋的黑客行为。我认为如果希望返回这样的值,应该使用命名的内部类,而不是匿名类。它是“更干净”的IMHO。这太脏了,我现在想洗个澡,就因为我读到了:)我只是在键盘上呕吐,然后出于不想创建holder类的懒惰,我就这么做了。@chzbrgla洗完澡后,下面的解决方案对我有效<代码>公共静态最终原子长结果=新原子长(0);结果集(st.getLong(4);)
Ok,甚至IntelliJ也建议这样做,但它看起来不像是标准解决方案……Work
是org.hibernate.jdbc中的一个接口,execute
的签名是public void execute(java.sql.Connection)
。它真的会吃掉你的回执吗?对不起,我没有得到你答案的第二部分(更新)。我对侦听器的内部匿名类有问题(因为它是重写,所以无法使它返回某些内容)。你基本上使用什么?或者我需要了解另外一个概念吗?还是我应该问一个全新的问题?有参考资料吗?我需要找到一些关于这种方法的文件?有吗?@Aritra,很难找到类似这样的引用,但我已经做了14年的java,实际上,当您需要从方法中获取返回值(除了它的实际返回值)时,您必须传递一个对象,并让该对象具有可以在返回时检查的属性。我的代码片段只是一种通用的方法,比使用数组或专用类来获取“out参数”更简洁。这有助于…+1