Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从匿名内部类设置外部变量_Java_Anonymous Class - Fatal编程技术网

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

在Java中,有没有办法从匿名内部类访问调用方范围的变量

以下是我需要的示例代码:

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

对此的标准解决方案是返回一个值。例如,请参见yeolde
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);
    }
}

这种情况在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