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




anonymous-class anonymous-inner-class (14)

जावा में अज्ञात वर्गों का उपयोग क्या है? क्या हम कह सकते हैं कि अज्ञात वर्ग का उपयोग जावा के फायदों में से एक है?


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

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

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

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

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

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

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

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


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

doc से उदाहरण पर विचार करें जहां हमारे पास एक 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 एक कार्यात्मक इंटरफ़ेस है। हम doc का उपयोग कर सकते हैं जो हमें कार्य तर्क के रूप में कार्य को पारित करने की अनुमति देता है:

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

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


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

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

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

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

एक "अज्ञात वर्ग" द्वारा, मैं इसे लेता हूं आप अज्ञात आंतरिक वर्ग का मतलब है।

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

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

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

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

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


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

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

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

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

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

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

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

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

संदर्भ: सैम 21 दिनों के सातवें संस्करण में स्वयं को जावा सिखाएं


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

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

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

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


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

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

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


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

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

}

ऐसा लगता है कि यहां कोई भी उल्लेख नहीं किया गया है लेकिन आप जेनेरिक टाइप तर्क (जो आमतौर पर टाइप एरर के कारण खो जाता है) रखने के लिए अज्ञात वर्ग का उपयोग भी कर सकते हैं:

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 उदाहरण में उत्तीर्ण प्रकार की गैर-त्रुटिपूर्ण परिभाषा होगी।

प्रयोग

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

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

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

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

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

बनाम

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

बहुत सारे वक्तव्य करते समय यह कुछ अनावश्यकता बचाता है। हालांकि, मैंने ऐसा करने में भी समस्याएं निभाई हैं जब बाहरी वर्ग को रिमोटिंग के माध्यम से क्रमबद्ध करने की आवश्यकता है।


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

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

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

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


बेनामी कक्षा के लिए गाइडलाइन।

  1. बेनामी क्लास घोषित और एक साथ शुरू किया गया है।

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

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

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

button.addActionListener(new ActionListener(){

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

    }
});

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

यह थ्रेड का उपयोग करते हुए अनाम आंतरिक प्रकार के उदाहरण में से एक है







anonymous-inner-class