जावा में एक विधि के अंदर वर्ग परिभाषाओं का उपयोग


105

उदाहरण:

public class TestClass {

    public static void main(String[] args) {
        TestClass t = new TestClass();
    }

    private static void testMethod() {
        abstract class TestMethod {
            int a;
            int b;
            int c;

            abstract void implementMe();
        }

        class DummyClass extends TestMethod {
            void implementMe() {}
        }

        DummyClass dummy = new DummyClass();
    }
}

मुझे पता चला कि जावा में कोड का उपरोक्त टुकड़ा पूरी तरह से कानूनी है। मेरे पास निम्नलिखित प्रश्न हैं।

  1. एक विधि के अंदर कभी वर्ग परिभाषा होने का क्या उपयोग है?
  2. के लिए एक क्लास फाइल जेनरेट की जाएगी DummyClass
  3. ऑब्जेक्ट ओरिएंटेड तरीके से इस अवधारणा की कल्पना करना मेरे लिए कठिन है। व्यवहार के अंदर एक वर्ग की परिभाषा होना। संभवतः कोई मुझे वास्तविक वास्तविक उदाहरणों के साथ बता सकता है।
  4. एक विधि के अंदर सार कक्षाएं मुझे थोड़ा पागल लगती हैं। लेकिन कोई इंटरफेस की अनुमति नहीं है। क्या इसके पीछे कोई कारण है?

1
मैं सहमत हूं, यह अविश्वसनीय रूप से गड़बड़ है। मैंने कुछ कोड का निरीक्षण किया जो मेरे सहयोगी ने लिखा था और इस स्थानीय वर्ग को एक विधि में पाया था ... इससे मुझे ऐसा महसूस हुआ कि यह मॉड्यूल पूरी तरह से अपवित्र था।
किसी ने

7
कभी-कभी यह सामान को छुपाने के बारे में अधिक होता है जो आपको लगने के बजाय कहीं और चाहिए;)
सॉरीमिसजैकसन

जवाबों:


71

इसे स्थानीय वर्ग कहा जाता है।

2 आसान है: हाँ, एक वर्ग फ़ाइल उत्पन्न की जाएगी।

1 और 3 एक ही तरह के प्रश्न हैं। आप एक स्थानीय वर्ग का उपयोग करेंगे, जहाँ आपको कभी भी किसी एक को तुरंत लागू करने या कहीं भी एक विधि में विवरण के बारे में जानने की आवश्यकता नहीं होगी।

एक विशिष्ट उपयोग कुछ इंटरफ़ेस का एक फेंक-दूर कार्यान्वयन बनाने के लिए होगा। उदाहरण के लिए आप अक्सर कुछ इस तरह देखेंगे:

  //within some method
  taskExecutor.execute( new Runnable() {
       public void run() {
            classWithMethodToFire.doSomething( parameter );
       }
  }); 

यदि आपको इनमें से एक गुच्छा बनाने और उनके साथ कुछ करने की आवश्यकता है, तो आप इसे बदल सकते हैं

  //within some method
  class myFirstRunnableClass implements Runnable {
       public void run() {
            classWithMethodToFire.doSomething( parameter );
       }
  }
  class mySecondRunnableClass implements Runnable {
       public void run() {
            classWithMethodToFire.doSomethingElse( parameter );
       }
  }
  taskExecutor.execute(new myFirstRunnableClass());
  taskExecutor.execute(new mySecondRunnableClass());

इंटरफेस के बारे में: मुझे यकीन नहीं है कि अगर कोई तकनीकी समस्या है जो स्थानीय रूप से परिभाषित इंटरफेस को कंपाइलर के लिए एक समस्या बनाती है, लेकिन अगर ऐसा नहीं है, तो भी वे कोई मूल्य नहीं जोड़ेंगे। यदि एक स्थानीय वर्ग जो एक स्थानीय इंटरफ़ेस को लागू करता है, तो इसका उपयोग विधि के बाहर किया जाता है, तो इंटरफ़ेस अर्थहीन होगा। और अगर एक स्थानीय वर्ग केवल विधि के अंदर उपयोग होने वाला था, तो इंटरफ़ेस और वर्ग दोनों को उस पद्धति के भीतर लागू किया जाएगा, इसलिए इंटरफ़ेस परिभाषा बेमानी होगी।


जावा स्थानीय कक्षाओं के किस संस्करण में कोई विचार पेश किया गया है?
स्लेज

1
जावा 1.1 में आंतरिक कक्षाएं जोड़ी गईं - मैं अनुमान लगा रहा हूं कि स्थानीय कक्षाएं भी थीं लेकिन मेरे पास उस पर दस्तावेज नहीं हैं।
याकूब मैटिसन

क्या आप एक गैर-अनाम स्थानीय वर्ग के उपयोग के मामले के लिए एक बेहतर उदाहरण प्रदान कर सकते हैं? अनाम वर्गों के साथ आपका दूसरा ब्लॉक कोड फिर से लिखा जा सकता है।
सेर्गेई पाऊक

1
गैर-अनाम स्थानीय वर्गों के अधिकांश उपयोग अनाम कक्षाओं के साथ पूरे किए जा सकते हैं। मैंने उदाहरण नहीं दिया, लेकिन यदि आप एक ही वर्ग प्रकार का एक और उदाहरण बनाना चाहते हैं, तो आप आमतौर पर एक नामित स्थानीय वर्ग का उपयोग करेंगे।
जैकब मैटीसन

1
ओपी के लिए: ध्यान दें कि स्थानीय वर्ग थ्रेड के लिए संवाद करने का एक तरीका प्रदान करता है - parameterउपरोक्त को एन्क्लोज़िंग विधि में घोषित किया जा सकता है, और दोनों थ्रेड द्वारा पहुँचा जा सकता है।
flow2k 22

15

जिन्हें स्थानीय वर्ग कहा जाता है । आप एक विस्तृत विवरण और एक उदाहरण पा सकते हैं यहाँ । उदाहरण एक विशिष्ट कार्यान्वयन देता है जिसे हमें विधि के बाहर जानने की आवश्यकता नहीं है।


2
महान लिंक (अभी भी 7+ साल बाद काम करता है!)। विशेष रूप से, ध्यान दें "सदस्य वर्गों की तरह, स्थानीय वर्ग एक युक्त उदाहरण के साथ जुड़े हुए हैं, और किसी भी सदस्य को शामिल कर सकते हैं, जिसमें शामिल वर्ग शामिल हैं ।"
flow2k

10
  1. विधि के बाहर से कक्षा को नहीं देखा जा सकता है (अर्थात त्वरित, उसके तरीके बिना परावर्तन के)। इसके अलावा, यह टेस्टमेथोड () में परिभाषित स्थानीय चर तक पहुंच सकता है, लेकिन वर्ग परिभाषा से पहले।

  2. मैंने वास्तव में सोचा था: "ऐसी कोई फ़ाइल नहीं लिखी जाएगी।" जब तक मैंने अभी कोशिश नहीं की: ओह हाँ, ऐसी फाइल बन गई है! इसे A $ 1B.class जैसा कुछ कहा जाएगा, जहां A बाहरी वर्ग है, और B स्थानीय वर्ग है।

  3. विशेष रूप से कॉलबैक फ़ंक्शंस के लिए (GUI में ईवेंट हैंडलर्स, जैसे onClick () जब एक बटन क्लिक किया जाता है आदि), तो "अनाम कक्षाएं" का उपयोग करना काफी सामान्य है - सबसे पहले क्योंकि आप उनमें से बहुत से समाप्त कर सकते हैं। लेकिन कभी-कभी अनाम कक्षाएं पर्याप्त नहीं होती हैं - विशेष रूप से, आप उन पर एक कंस्ट्रक्टर को परिभाषित नहीं कर सकते हैं। इस मामले में, ये विधि स्थानीय कक्षाएं एक अच्छा विकल्प हो सकती हैं।


2
2. एहराम, यकीन है कि यह होगा। आपकी java फ़ाइल में प्रत्येक नेस्टेड, स्थानीय या अनाम वर्ग के लिए क्लास फाइलें जनरेट होंगी।
sepp2k

2
2. "ऐसी कोई फ़ाइल नहीं लिखी जाएगी।" -- ये गलत है। यह बनाता है TestClass$1TestMethodClass.class, कैसे आंतरिक वर्गों .classफ़ाइलों के नाम के अनुरूप हैं।
पॉलीजेन लुब्रिकेंट 20

अच्छा उत्तर, 2 के लिए अपवाद: आपको अनाम वर्ग मिलेगा, इस मामले में "TestClass $ 1TestMethodClass.class"
स्टीव बी

हां मुझे माफ कर दो! मुझे कुछ सेकंड पहले तक इसका एहसास नहीं था। आप रहते हैं और सीखते हैं :-))
क्रिस लेचर 20

अनाम और स्थानीय कक्षाओं के बीच अंतर को उजागर करने के लिए आपको मेरा +1 मिला है: एक कंस्ट्रक्टर को परिभाषित करना।
मैथ्यू

7

इसका वास्तविक उद्देश्य हमें फ़ंक्शन कॉल में क्लास इनलाइन बनाने की अनुमति देना है ताकि हम उन लोगों को सांत्वना दे सकें जो यह दिखावा करना पसंद करते हैं कि हम एक कार्यात्मक भाषा में लिख रहे हैं;)


4

एक ही मामला है जब आप एक पूर्ण विकसित कार्य करना चाहेंगे आंतरिक वर्ग बनाम अनाम वर्ग (उर्फ जावा बंद) जब निम्न स्थितियां पूरी होती हैं

  1. आपको एक इंटरफ़ेस या अमूर्त वर्ग कार्यान्वयन की आपूर्ति करने की आवश्यकता है
  2. आप कॉलिंग फ़ंक्शन में परिभाषित कुछ अंतिम मापदंडों का उपयोग करना चाहते हैं
  3. आपको इंटरफ़ेस कॉल के निष्पादन की कुछ स्थिति को रिकॉर्ड करने की आवश्यकता है।

जैसे कि कोई व्यक्ति चाहता है Runnableऔर आप उस समय को रिकॉर्ड करना चाहते हैं जब निष्पादन शुरू हुआ और समाप्त हो गया।

अनाम वर्ग के साथ ऐसा करना संभव नहीं है, आंतरिक वर्ग के साथ आप ऐसा कर सकते हैं।

यहाँ एक उदाहरण है जो मेरी बात को प्रदर्शित करता है

private static void testMethod (
        final Object param1,
        final Object param2
    )
{
    class RunnableWithStartAndEnd extends Runnable{
        Date start;
        Date end;

        public void run () {
            start = new Date( );
            try
            {
                evalParam1( param1 );
                evalParam2( param2 );
                ...
            }
            finally
            {
                end = new Date( );
            }
        }
    }

    final RunnableWithStartAndEnd runnable = new RunnableWithStartAndEnd( );

    final Thread thread = new Thread( runnable );
    thread.start( );
    thread.join( );

    System.out.println( runnable.start );
    System.out.println( runnable.end );
}

हालांकि इस पैटर्न का उपयोग करने से पहले, कृपया मूल्यांकन करें कि क्या सादे पुराने टॉप-लेवल क्लास, या इनर क्लास, या स्टैटिक इनर क्लास बेहतर विकल्प हैं।


मैं फ़ंक्शंस से रिटर्न वैल्यू असाइन करने के लिए # 2 का बहुत दुरुपयोग करता हूं।
एडी बी

2

आंतरिक वर्गों (एक विधि या वर्ग के भीतर) को परिभाषित करने का मुख्य कारण संलग्न वर्ग और विधि के सदस्यों और चर की पहुंच से निपटना है। एक आंतरिक वर्ग निजी डेटा सदस्यों को देख सकता है और उन पर काम कर सकता है। यदि एक विधि के भीतर यह अंतिम स्थानीय चर के साथ भी सौदा कर सकते हैं।

आंतरिक कक्षाएं होने से यह सुनिश्चित करने में मदद मिलती है कि यह वर्ग बाहरी दुनिया के लिए सुलभ नहीं है। यह विशेष रूप से GWT या GXT आदि में UI प्रोग्रामिंग के मामलों के लिए सही है जहाँ JS जनरेटिंग कोड जावा में लिखा गया है और प्रत्येक बटन या घटना के लिए व्यवहार को अनाम वर्ग बनाकर परिभाषित किया जाना है


1

मैं वसंत में एक अच्छे उदाहरण के साथ आया हूं। फ्रेमवर्क एक समान तरीके से विभिन्न डेटाबेस संचालन से निपटने के लिए विधि के अंदर स्थानीय वर्ग की परिभाषाओं की अवधारणा का उपयोग कर रहा है।

मान लें कि आपके पास एक कोड है:

JdbcTemplate jdbcOperations = new JdbcTemplate(this.myDataSource);
jdbcOperations.execute("call my_stored_procedure()")
jdbcOperations.query(queryToRun, new MyCustomRowMapper(), withInputParams);
jdbcOperations.update(queryToRun, withInputParams);

आइए सबसे पहले क्रियान्वयन के कार्यान्वयन को देखें ():

    @Override
    public void execute(final String sql) throws DataAccessException {
        if (logger.isDebugEnabled()) {
            logger.debug("Executing SQL statement [" + sql + "]");
        }

        /**
         * Callback to execute the statement.
         (can access method local state like sql input parameter)
         */
        class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
            @Override
            @Nullable
            public Object doInStatement(Statement stmt) throws SQLException {
                stmt.execute(sql);
                return null;
            }
            @Override
            public String getSql() {
                return sql;
            }
        }

        //transforms method input into a functional Object
        execute(new ExecuteStatementCallback());
    }

कृपया अंतिम पंक्ति नोट करें। बाकी तरीकों के लिए भी वसंत इस सटीक "चाल" को करता है:

//uses local class QueryStatementCallback implements StatementCallback<T>, SqlProvider
jdbcOperations.query(...) 
//uses local class UpdateStatementCallback implements StatementCallback<Integer>, SqlProvider
jdbcOperations.update(...)

स्थानीय वर्गों के साथ "ट्रिक" फ्रेमवर्क उन सभी परिदृश्यों को एक एकल विधि से निपटने की अनुमति देता है जो स्टेटमेंटकॉल इंटरफ़ेस के माध्यम से उन वर्गों को स्वीकार करते हैं। यह एकल विधि क्रियाओं के बीच एक सेतु का कार्य करती है (निष्पादित, अद्यतन) और उनके आसपास के सामान्य संचालन (जैसे निष्पादन, कनेक्शन प्रबंधन, त्रुटि अनुवाद और dbms कंसोल आउटपुट)

public <T> T execute(StatementCallback<T> action) throws DataAccessException    {
        Assert.notNull(action, "Callback object must not be null");

        Connection con = DataSourceUtils.getConnection(obtainDataSource());
        Statement stmt = null;
        try {
            stmt = con.createStatement();
            applyStatementSettings(stmt);
            //
            T result = action.doInStatement(stmt);
            handleWarnings(stmt);
            return result;
        }
        catch (SQLException ex) {
            // Release Connection early, to avoid potential connection pool deadlock
            // in the case when the exception translator hasn't been initialized yet.
            String sql = getSql(action);
            JdbcUtils.closeStatement(stmt);
            stmt = null;
            DataSourceUtils.releaseConnection(con, getDataSource());
            con = null;
            throw translateException("StatementCallback", sql, ex);
        }
        finally {
            JdbcUtils.closeStatement(stmt);
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.