java - अज्ञात आंतरिक वर्ग से बाहरी चर सेट करना




anonymous-class (6)

जावा में अज्ञात भीतरी कक्षा से कॉलर-स्कोप्ड चर का उपयोग करने का कोई तरीका है?

मुझे समझने के लिए नमूना कोड यहां दिया गया है:

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

कोड एक डीएओ सेवा वर्ग में है। जाहिर है, यह संकलित नहीं होता है, क्योंकि यह पूछता है कि result अंतिम है, यदि यह है - यह संकलित नहीं होता है क्योंकि मैं अंतिम var को संशोधित करने का प्रयास करता हूं। मैं जेडीके 5 से जुड़ा हूं। doWork() पूरी तरह से छोड़ने के doWork() , क्या परिणाम मूल्य को doWork() भीतर से सेट करने का कोई तरीका है?


इसका मानक समाधान एक मूल्य वापस करना है। उदाहरण के लिए, आप पुराने 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 किसी तृतीय-पक्ष पुस्तकालय से है और इसे बदला नहीं जा सकता है। तो मैं इसका उपयोग नहीं करने का सुझाव देता हूं, कम से कम अपने आवेदन को अलग करें ताकि आप इसका उपयोग सीधे नहीं कर रहे हों। कुछ इस तरह:

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

ऐसा करने का आपका सबसे आसान (और सबसे साफ) तरीका जावा 1.5 के बाद AtomicLong का उपयोग AtomicLong है

public Long getNumber(final String type, final String refNumber, final Long year) throws ServiceException {
   final AtomicLong result = new AtomicLong;
   try {
       Session session = PersistenceHelper.getSession();
       session.doWork(new Work() {
               public void execute(Connection conn) throws SQLException {
                   //...
                   result.set(4);
                   //...
               }
           });
   } catch (Exception e) {
       log.error(e);
   }
   return result.get;
}

Java.util.concurrent.atomic पैकेज में उपलब्ध अन्य AtomicXXX वेरिएंट हैं: AtomicInteger , AtomicReference<V> (for your POJOs) , AtomicReference<V> (for your POJOs) आदि


बेनामी वर्ग / विधियां बंद नहीं होती हैं - यह बिल्कुल अंतर है।

समस्या यह है कि doWork() को कॉल execute() लिए एक नया थ्रेड बना सकता है और परिणाम प्राप्त होने से पहले getNumber() वापस लौटा सकता है - और इससे भी अधिक समस्याग्रस्त: जहां execute() करना चाहिए execute() परिणाम को लिखना चाहिए जब स्टैक फ्रेम जिसमें वेरिएबल है चला गया? बंद होने वाली भाषाओं को अपने मूल दायरे से बाहर ऐसे चर को जीवित रखने के लिए एक तंत्र शुरू करना है (या सुनिश्चित करें कि बंद को अलग थ्रेड में निष्पादित नहीं किया गया है)।

एक कामकाज:

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

यदि युक्त वर्ग MyClass -> है

MyClass.this.variable = value;

याद रखें कि यह एक निजी चर के साथ काम करेगा (मुझे लगता है कि यह काम करेगा)।

केवल वर्ग (वर्ग चर) के गुणों के लिए काम करता है। विधि स्थानीय चर के लिए काम नहीं करता है। जेएसई 7 में शायद उस तरह की चीज करने के लिए बंद हो जाएगा।


लंबे समय तक अपरिवर्तनीय है। यदि आप एक म्यूटेबल क्लास का उपयोग करते हैं, तो एक लंबा मूल्य रखते हुए, आप मान बदल सकते हैं। उदाहरण के लिए:

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

}

हाइबरनेट 4 के रूप में, विधि Session#doReturningWork(ReturningWork<T> work) आंतरिक विधि से रिटर्न वैल वापस कर देगा:

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

(जावा 8 लैम्ब्डा का उपयोग करके साफ किया गया)





anonymous-class