बेनामी आंतरिक कक्षाएं जावा में कैसे उपयोग की जाती हैं?


309

जावा में अनाम कक्षाओं का उपयोग क्या है? क्या हम कह सकते हैं कि अनाम वर्ग का उपयोग जावा के लाभों में से एक है?


67
यह जावा का इतना लाभ नहीं है क्योंकि यह जावा में बंद होने की कमी के आसपास काम करने का एक तरीका है।
एरिक विल्सन

4
Java 8 ने लैम्बडा एक्सप्रेशन भी पेश किए हैं। उत्तर की जाँच करें: stackoverflow.com/questions/355167/…
akhil_mittal

जवाबों:


369

एक "अनाम वर्ग" से, मुझे लगता है कि इसका मतलब है अनाम आंतरिक वर्ग

एक अनाम आंतरिक वर्ग उपयोगी तब आ सकता है जब किसी वस्तु का उदाहरण "एक्स्ट्रा" के साथ हो, जैसे कि ओवरराइड करने के तरीके, वास्तव में एक वर्ग को उपवर्ग के बिना।

मैं इवेंट श्रोता को संलग्न करने के लिए शॉर्टकट के रूप में इसका उपयोग करता हूं:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do something
    }
});

इस विधि का उपयोग करने से कोडिंग थोड़ी तेज हो जाती है, क्योंकि मुझे एक अतिरिक्त वर्ग बनाने की आवश्यकता नहीं है ActionListener- मैं वास्तव में एक अलग वर्ग बनाने के बिना एक अनाम आंतरिक वर्ग को त्वरित कर सकता हूं।

मैं केवल "त्वरित और गंदे" कार्यों के लिए इस तकनीक का उपयोग करता हूं, जहां पूरी कक्षा को अनावश्यक लगता है। कई अनाम आंतरिक वर्ग जो वास्तव में एक ही काम करते हैं, को वास्तविक कक्षा में बदल दिया जाना चाहिए, यह एक आंतरिक वर्ग या एक अलग वर्ग होना चाहिए।


5
या आप गुमनाम आंतरिक वर्गों के साथ एक विधि में डुप्लिकेट अनाम आंतरिक कक्षाओं को रिफ्लेक्टर कर सकते हैं (और संभवतः कुछ अन्य डुप्लिकेट कोड)।
टॉम हॉकिन -

3
शानदार जवाब लेकिन एक त्वरित सवाल। क्या इसका मतलब यह है कि जावा गुमनाम आंतरिक वर्गों के बिना रह सकता है और वे लेने के लिए अतिरिक्त विकल्प की तरह हैं?
realPK

5
बहुत अच्छी तरह से समझाया, यहां तक ​​कि कठिन मैं किसी को भी पढ़ने के लिए सुझाव दूंगा कि यह देखने के लिए और देखें कि जावा 8 और लैम्ब्डा अभिव्यक्तियां कोडिंग को तेज और अधिक पठनीय बनाने के लिए क्या कर सकती हैं।
पिविस

2
@ user2190639 संक्षेप में, Java8 में लैम्ब्डा के
bonCodigo

3
आप क्यों कहा overloading methodsऔर नहीं overriding methods?
तरुण

73

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

public interface F<A, B> {
   B f(A a);
}

जावा में प्रथम श्रेणी का कार्य बनाने के लिए आप इसे गुमनाम रूप से उपयोग कर सकते हैं । मान लें कि आपके पास निम्न विधि है जो दी गई सूची में आई की तुलना में पहले नंबर को बड़ा करती है, या यदि कोई संख्या बड़ी नहीं है:

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return i;
}

और फिर आपके पास एक और तरीका है जो दिए गए सूची में पहले नंबर को छोटा करता है, या अगर कोई संख्या छोटी नहीं है, तो:

public static int smaller(final List<Integer> ns, final int i) {
   for (Integer n : ns)
      if (n < i)
         return n;
   return i;
}

ये विधियां लगभग समान हैं। प्रथम श्रेणी के फ़ंक्शन प्रकार F का उपयोग करते हुए, हम इन्हें निम्न तरीके से एक विधि में फिर से लिख सकते हैं:

public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
   for (T t : ts)
      if (f.f(t))
         return t;
   return z;
}

आप FirstMatch विधि का उपयोग करने के लिए एक अनाम वर्ग का उपयोग कर सकते हैं:

F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
   Boolean f(final Integer n) {
      return n > 10;
   }
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);

यह एक बहुत ही विपरीत उदाहरण है, लेकिन यह देखने में आसान है कि कार्यों को पास करने में सक्षम होने के रूप में मानो वे एक बहुत ही उपयोगी विशेषता है। खुद योएल द्वारा "कैन योर प्रोग्रामिंग लैंग्वेज डू दिस" देखें ।

इस शैली में जावा प्रोग्रामिंग के लिए एक अच्छा पुस्तकालय: कार्यात्मक जावा।


20
दुर्भाग्य से, जावा, आईएमएचओ में कार्यात्मक प्रोग्रामिंग करने की क्रिया, इसके लाभ को आगे बढ़ाती है - कार्यात्मक प्रोग्रामिंग के हड़ताली बिंदुओं में से एक यह है कि यह कोड आकार को कम करता है, और चीजों को पढ़ना और संशोधित करना आसान बनाता है। लेकिन कार्यात्मक जावा doesn 't ऐसा लगता है।
Chii

27
जावा की थकावट के साथ कार्यात्मक प्रोग्रामिंग की सभी समझ!
एडम जास्कविज़

3
मेरे अनुभव में, जावा में कार्यात्मक शैली को सामने की ओर वर्बोसिटी के साथ भुगतान किया जाता है, लेकिन यह लंबे समय में संक्षिप्तता पैदा करता है। उदाहरण के लिए, myList.map (f) लूप के लिए संबंधित की तुलना में काफी कम क्रिया है।
सर्वनाश

2
स्काला , एक कार्यात्मक प्रोग्रामिंग शैली भाषा, कथित तौर पर JVM के अंदर अच्छी तरह से चलती है और कार्यात्मक-प्रोग्रामिंग परिदृश्यों के लिए एक विकल्प हो सकता है।
डेरेल टेग्यू

51

अनाम आंतरिक वर्ग का उपयोग निम्नलिखित परिदृश्य में किया जाता है:

1.) ओवरराइडिंग (सब क्लासिंग) के लिए, जब वर्तमान मामले को छोड़कर कक्षा की परिभाषा उपयोगी नहीं है:

class A{
   public void methodA() {
      System.out.println("methodA");
    }
}
class B{
    A a = new A() {
     public void methodA() {
        System.out.println("anonymous methodA");
     }
   };
}

2.) इंटरफ़ेस लागू करने के लिए, जब इंटरफ़ेस का कार्यान्वयन केवल वर्तमान मामले के लिए आवश्यक है:

interface interfaceA{
   public void methodA();
}
class B{
   interfaceA a = new interfaceA() {
     public void methodA() {
        System.out.println("anonymous methodA implementer");
     }
   };
}

3.) तर्क परिभाषित बेनामी आंतरिक वर्ग:

 interface Foo {
   void methodFoo();
 }
 class B{
  void do(Foo f) { }
}

class A{
   void methodA() {
     B b = new B();
     b.do(new Foo() {
       public void methodFoo() {
         System.out.println("methodFoo");
       } 
     });
   } 
 } 

8
शानदार उत्तर, जैसा दिखता है 3.) इवेंट श्रोताओं के लिए इस्तेमाल किया जाने वाला पैटर्न है
xdl

47

मैं मानचित्र मानचित्रण के लिए सिंटैक्स हैक के रूप में कभी-कभी उनका उपयोग करता हूं:

Map map = new HashMap() {{
   put("key", "value");
}};

बनाम

Map map = new HashMap();
map.put("key", "value");

बहुत सारे पुट बयान करते समय यह कुछ अतिरेक बचाता है। हालाँकि, मैं इसे करने में आने वाली समस्याओं में भी भाग ले चुका हूँ, जब बाहरी वर्ग को रीमोटिंग के माध्यम से क्रमबद्ध करना पड़ता है।


56
स्पष्ट होने के लिए, ब्रेसिज़ का पहला सेट अनाम आंतरिक वर्ग (सबक्लासिंग हाशप) है। ब्रेसिज़ का दूसरा सेट एक उदाहरण इनिशलाइज़र है (एक स्थिर एक के बजाय) जो तब आपके हाशप उप-वर्ग पर मान सेट करता है। इसका उल्लेख करने के लिए +1, नोज के लिए इसे वर्तनी के लिए -1 नहीं। ;
-D

4
डबल-ब्रेस सिंटैक्स के बारे में और अधिक पढ़ें यहाँ
मार्टिन एंडरसन


18

वे आमतौर पर कॉलबैक की क्रिया रूप के रूप में उपयोग किए जाते हैं।

मुझे लगता है कि आप कह सकते हैं कि उनके पास न होने की तुलना में एक फायदा है, और हर बार नामांकित वर्ग बनाने के लिए, लेकिन अन्य भाषाओं में समान अवधारणाओं को बेहतर तरीके से लागू किया जाता है (जैसे क्लोजर या ब्लॉक)

यहाँ एक स्विंग उदाहरण है

myButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        // do stuff here...
    }
});

हालाँकि यह अभी भी गड़बड़ है, यह आपको इस तरह के हर फेंक श्रोता के लिए एक नामित वर्ग को परिभाषित करने के लिए मजबूर करने से बहुत बेहतर है (हालांकि स्थिति और पुन: उपयोग के आधार पर, यह अभी भी बेहतर दृष्टिकोण हो सकता है)


1
क्या आपके कहने का मतलब था? यदि यह क्रिया होगी, तो कॉलबैक अलग-अलग रहेगा, जिससे यह थोड़ा बड़ा हो जाएगा, और इस प्रकार यह क्रिया बन जाएगा। यदि आप कहते हैं कि यह अभी भी क्रिया है, तो एक स्वरुप क्या होगा?
user3081519

1
@ user3081519, कुछ इस तरह से myButton.addActionListener(e -> { /* do stuff here */})या myButton.addActionListener(stuff)terser होगा।
सैमुअल एडविन वार्ड

8

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

विचार यह है कि आप उन्हें किसी फ़ंक्शन के कोड के अंदर से कॉल करते हैं ताकि आप उन्हें कहीं और न देखें, इसलिए आपको उन्हें नाम देने की आवश्यकता नहीं है। संकलक बस उन्हें enumerates।

वे अनिवार्य रूप से सिंथेटिक चीनी हैं, और आम तौर पर बड़े होने के साथ कहीं और स्थानांतरित किए जाने चाहिए।

मुझे यकीन नहीं है कि यह जावा के फायदों में से एक है, हालांकि यदि आप उनका उपयोग करते हैं (और हम सभी अक्सर उनका उपयोग करते हैं, दुर्भाग्य से), तो आप तर्क दे सकते हैं कि वे एक हैं।


6

अनाम वर्ग के लिए गाइडलाइन।

  1. बेनामी वर्ग एक साथ घोषित और आरंभिक है।

  2. अनाम वर्ग को एक या केवल एक वर्ग या इंटरफ़ेस सम्मान का विस्तार या कार्यान्वयन करना चाहिए।

  3. चूंकि अनाम वर्ग का कोई नाम नहीं है, इसलिए इसे केवल एक बार उपयोग किया जा सकता है।

उदाहरण के लिए:

button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub

    }
});

# 3 के बारे में: पूरी तरह से सच नहीं है। आप प्रतिबिंब के साथ एक अनाम वर्ग के कई उदाहरण प्राप्त कर सकते हैं, जैसे ref.getClass().newInstance()
icza

नियम सवाल का जवाब नहीं देते।
लोर्ने

5

हां, अनाम आंतरिक कक्षाएं निश्चित रूप से जावा के फायदों में से एक हैं।

एक अनाम आंतरिक वर्ग के साथ आपके पास आसपास के वर्ग के अंतिम और सदस्य चर तक पहुंच है, और यह श्रोताओं आदि के काम में आता है।

लेकिन एक प्रमुख लाभ यह है कि आंतरिक वर्ग कोड, जो (कम से कम होना चाहिए) कसकर आसपास के वर्ग / विधि / ब्लॉक से जुड़ा होता है, का एक विशिष्ट संदर्भ (आसपास का वर्ग, विधि और ब्लॉक) होता है।


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

5
new Thread() {
        public void run() {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                System.out.println("Exception message: " + e.getMessage());
                System.out.println("Exception cause: " + e.getCause());
            }
        }
    }.start();

यह भी धागे का उपयोग कर गुमनाम आंतरिक प्रकार के लिए एक उदाहरण है


3

मैं नए धागे कॉल करने के लिए अनाम वस्तुओं का उपयोग करें ..

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();

3

एक आंतरिक वर्ग बाहरी वर्ग के एक उदाहरण के साथ जुड़ा हुआ है और दो विशेष प्रकार हैं: स्थानीय वर्ग और अनाम वर्ग । एक अनाम वर्ग हमें एक ही समय में एक वर्ग घोषित करने और तत्काल करने में सक्षम बनाता है, इसलिए कोड को संक्षिप्त बनाता है। हम उनका उपयोग तब करते हैं जब हमें एक स्थानीय वर्ग की आवश्यकता होती है जब उनका नाम नहीं होता है।

डॉक्टर के पास एक उदाहरण पर विचार करें जहां हमारा एक Personवर्ग है:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public void printPerson() {
        // ...
    }
}

और हमारे पास खोज मापदंड से मेल खाने वाले सदस्यों को मुद्रित करने की एक विधि है:

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

CheckPersonइंटरफ़ेस कहाँ है:

interface CheckPerson {
    boolean test(Person p);
}

अब हम अनाम वर्ग का उपयोग कर सकते हैं जो खोज मापदंड को निर्दिष्ट करने के लिए इस इंटरफ़ेस को लागू करता है:

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

यहां इंटरफ़ेस बहुत सरल है और अनाम वर्ग का वाक्यविन्यास अस्पष्ट और अस्पष्ट लगता है।

जावा 8 ने एक कार्यात्मक फंक्शनल शब्द पेश किया है, जो केवल एक सार पद्धति वाला एक इंटरफ़ेस है, इसलिए हम कह सकते हैं कि CheckPersonयह एक कार्यात्मक इंटरफ़ेस है। हम लैम्ब्डा एक्सप्रेशन का उपयोग कर सकते हैं जो हमें विधि तर्क के रूप में फ़ंक्शन को पास करने की अनुमति देता है:

printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

हम इंटरफ़ेस Predicateके स्थान पर एक मानक कार्यात्मक इंटरफ़ेस का उपयोग कर सकते हैं CheckPerson, जो आवश्यक कोड की मात्रा को और कम कर देगा।


2

विभिन्न वस्तुओं के लिए अलग-अलग कार्यान्वयन देते समय अनाम आंतरिक वर्ग फायदेमंद हो सकता है। लेकिन बहुत ही संयम से इस्तेमाल किया जाना चाहिए क्योंकि यह प्रोग्राम पठनीयता के लिए समस्या पैदा करता है।


1

कक्षा-फाइनल में अनाम कक्षाओं के प्रमुख उपयोग में से एक जिसे अंतिम संरक्षक कहा जाता है । जावा दुनिया में अंतिम तरीकों का उपयोग करने से बचना चाहिए जब तक कि आपको वास्तव में उनकी आवश्यकता न हो। आपको याद रखना होगा, जब आप उप-वर्गों के लिए अंतिम विधि को ओवरराइड करते हैं, तो आपको हमेशा ही लागू करना चाहिए super.finalize(), क्योंकि सुपर क्लास का अंतिम तरीका स्वचालित रूप से नहीं होगा और आपको मेमोरी लीक की समस्या हो सकती है।

इसलिए ऊपर वर्णित तथ्य को देखते हुए, आप बस अनाम वर्गों का उपयोग कर सकते हैं जैसे:

public class HeavyClass{
    private final Object finalizerGuardian = new Object() {
        @Override
        protected void finalize() throws Throwable{
            //Finalize outer HeavyClass object
        }
    };
}

इस तकनीक का उपयोग करके आप अपने और अपने दूसरे डेवलपर्स को राहत देने के लिए super.finalize()प्रत्येक उप-वर्ग पर कॉल कर सकते हैं, HeavyClassजिसकी अंतिम विधि की आवश्यकता है।


1

आप इस तरह से अनाम वर्ग का उपयोग कर सकते हैं

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});

1

यहाँ किसी का उल्लेख नहीं किया गया है, लेकिन आप सामान्य प्रकार के तर्क को रखने के लिए अनाम वर्ग का भी उपयोग कर सकते हैं (जो सामान्य रूप से प्रकार के क्षरण के कारण खो गया है) :

public abstract class TypeHolder<T> {
    private final Type type;

    public TypeReference() {
        // you may do do additional sanity checks here
        final Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public final Type getType() {
        return this.type;
    }
}

यदि आप अनाम तरीके से इस वर्ग को तुरंत भेज देंगे

TypeHolder<List<String>, Map<Ineger, Long>> holder = 
    new TypeHolder<List<String>, Map<Ineger, Long>>() {};

फिर ऐसे holder उदाहरण में पारित प्रकार की गैर-मिटाई गई परिभाषा होगी।

प्रयोग

यह सत्यापनकर्ता / डिसेरिएलिज़ेटर्स के निर्माण के लिए बहुत उपयोगी है। इसके अलावा आप प्रतिबिंब के साथ जेनेरिक प्रकार को तुरंत कर सकते हैं (इसलिए यदि आप कभी भी new T()पैरामीट्रिक प्रकार में करना चाहते थे - आपका स्वागत है)

कमियां / सीमाएं

  1. आपको स्पष्ट रूप से सामान्य पैरामीटर पास करना चाहिए। ऐसा करने में विफल रहने से टाइप पैरामीटर नुकसान होगा
  2. प्रत्येक तात्कालिकता आपको संकलक द्वारा उत्पन्न की जाने वाली अतिरिक्त कक्षा का खर्च करेगी जो कि क्लासपैथ प्रदूषण / जार ब्लोटिंग की ओर ले जाती है

1

कोड को ऑप्टिमाइज़ करने का सबसे अच्छा तरीका। इसके अलावा, हम एक वर्ग या इंटरफ़ेस की ओवरराइडिंग विधि के लिए उपयोग कर सकते हैं।

import java.util.Scanner;
abstract class AnonymousInner {
    abstract void sum();
}

class AnonymousInnerMain {
    public static void main(String []k){
        Scanner sn = new Scanner(System.in);
        System.out.println("Enter two vlaues");
            int a= Integer.parseInt(sn.nextLine());
            int b= Integer.parseInt(sn.nextLine()); 
        AnonymousInner ac = new AnonymousInner(){
            void sum(){
                int c= a+b;
                System.out.println("Sum of two number is: "+c);
            }
        };
        ac.sum();
    }

}

1

एक अनाम इनर क्लास का उपयोग एक ऐसी वस्तु बनाने के लिए किया जाता है जिसे फिर से संदर्भित नहीं किया जाएगा। इसका कोई नाम नहीं है और एक ही बयान में घोषित और बनाया गया है। यह वह जगह है जहाँ आप सामान्य रूप से किसी वस्तु के चर का उपयोग करेंगे। आप newकीवर्ड के साथ वेरिएबल को बदलते हैं , एक कंस्ट्रक्टर को कॉल करते हैं और क्लास की परिभाषा को अंदर {और }

जावा में थ्रेडेड प्रोग्राम लिखते समय, यह आमतौर पर इस तरह दिखेगा

ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();

यहां ThreadClassउपयोग उपयोगकर्ता परिभाषित किया जाएगा। यह वर्ग उन Runnableइंटरफ़ेस को लागू करेगा जो थ्रेड बनाने के लिए आवश्यक है। में विधि (केवल में विधि ) के रूप में अच्छी तरह से लागू किया जाना चाहिए। यह स्पष्ट है कि छुटकारा पाना अधिक कुशल होगा और यही कारण है कि बेनामी इनर क्लासेस मौजूद हैं।ThreadClassrun()RunnableThreadClass

निम्नलिखित कोड को देखें

Thread runner = new Thread(new Runnable() {
    public void run() {
        //Thread does it's work here
    }
});
runner.start();

यह कोड taskसबसे ऊपर के उदाहरण में किए गए संदर्भ को बदल देता है । एक अलग वर्ग होने के बजाय, Thread()निर्माणकर्ता के अंदर बेनामी इनर क्लास एक अनाम वस्तु देता है जो Runnableइंटरफ़ेस को लागू करता है और run()विधि को ओवरराइड करता है। विधि run()में कथन शामिल होंगे जो थ्रेड द्वारा आवश्यक कार्य करते हैं।

बेनामी इनर क्लासेस जावा के फायदों में से एक है, इस सवाल का जवाब देते हुए, मुझे यह कहना होगा कि मुझे यकीन नहीं है क्योंकि मैं इस समय कई प्रोग्रामिंग भाषाओं से परिचित नहीं हूं। लेकिन मैं जो कह सकता हूं वह निश्चित रूप से कोडिंग का एक तेज और आसान तरीका है।

संदर्भ: Sams 21 दिनों के सातवें संस्करण में अपने आप को जावा सिखाओ


0

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.