क्या जावा भाषा में प्रतिनिधि विशेषताएं हैं, कैसे सी # प्रतिनिधियों के लिए समर्थन है?
क्या जावा भाषा में प्रतिनिधि विशेषताएं हैं, कैसे सी # प्रतिनिधियों के लिए समर्थन है?
जवाबों:
नहीं वास्तव में कोई नहीं।
आप विधि ऑब्जेक्ट्स को प्राप्त करने के लिए प्रतिबिंब का उपयोग करके उसी प्रभाव को प्राप्त करने में सक्षम हो सकते हैं जिसे आप फिर से लागू कर सकते हैं, और दूसरा तरीका एकल 'इनवोक' या 'निष्पादित' विधि के साथ एक इंटरफ़ेस बनाना है, और फिर उन्हें विधि को कॉल करने के लिए त्वरित करें आपकी रुचि (यानी एक अनाम आंतरिक वर्ग का उपयोग करके)।
आपको यह लेख भी रोचक / उपयोगी लग सकता है : A Java Programmer C # Delegates (@kut.org) में दिखता है
आपके द्वारा दिए गए अर्थ के आधार पर, आप रणनीति पैटर्न का उपयोग करके एक समान प्रभाव (किसी विधि के आसपास से गुजरना) प्राप्त कर सकते हैं।
एक लाइन के बजाय इस तरह एक नामित विधि हस्ताक्षर की घोषणा:
// C#
public delegate void SomeFunction();
एक इंटरफ़ेस घोषित करें:
// Java
public interface ISomeBehaviour {
void SomeFunction();
}
विधि के ठोस कार्यान्वयन के लिए, उस वर्ग को परिभाषित करें जो व्यवहार को लागू करता है:
// Java
public class TypeABehaviour implements ISomeBehaviour {
public void SomeFunction() {
// TypeA behaviour
}
}
public class TypeBBehaviour implements ISomeBehaviour {
public void SomeFunction() {
// TypeB behaviour
}
}
फिर जहाँ भी आपको SomeFunction
C # में एक प्रतिनिधि मिला होगा, उसके ISomeBehaviour
बजाय एक संदर्भ का उपयोग करें:
// C#
SomeFunction doSomething = SomeMethod;
doSomething();
doSomething = SomeOtherMethod;
doSomething();
// Java
ISomeBehaviour someBehaviour = new TypeABehaviour();
someBehaviour.SomeFunction();
someBehaviour = new TypeBBehaviour();
someBehaviour.SomeFunction();
अनाम आंतरिक कक्षाओं के साथ, आप अलग-अलग नामित वर्गों की घोषणा करने से भी बच सकते हैं और लगभग उन्हें वास्तविक प्रतिनिधि कार्यों की तरह व्यवहार कर सकते हैं।
// Java
public void SomeMethod(ISomeBehaviour pSomeBehaviour) {
...
}
...
SomeMethod(new ISomeBehaviour() {
@Override
public void SomeFunction() {
// your implementation
}
});
यह संभवतः केवल तब उपयोग किया जाना चाहिए जब कार्यान्वयन वर्तमान संदर्भ के लिए बहुत विशिष्ट है और पुन: उपयोग होने से लाभ नहीं होगा।
और फिर जावा 8 में निश्चित रूप से, ये मूल रूप से मेमने के भाव बन जाते हैं:
// Java 8
SomeMethod(() -> { /* your implementation */ });
परिचय
Microsoft Visual J ++ विकास वातावरण का नवीनतम संस्करण एक भाषा निर्माण का समर्थन करता है जिसे प्रतिनिधि या बाध्य विधि संदर्भ कहा जाता है । यह निर्माण, और नए कीवर्ड
delegate
औरmulticast
इसका समर्थन करने के लिए पेश किए गए, जावा टीएम प्रोग्रामिंग भाषा का हिस्सा नहीं हैं, जो कि जावा भाषा विनिर्देश द्वारा निर्दिष्ट है और जेडीकेटीएम 1.1 सॉफ्टवेयर के लिए प्रलेखन में शामिल इनर क्लासेस विनिर्देश द्वारा संशोधित है ।यह संभावना नहीं है कि जावा प्रोग्रामिंग भाषा कभी भी इस निर्माण को शामिल करेगी। सन 1996 में पहले से ही ध्यान से इसे अपनाने और कार्यशील प्रोटोटाइप को त्यागने की सीमा तक माना जाता था। हमारा निष्कर्ष यह था कि बाध्य विधि संदर्भ भाषा के लिए अनावश्यक और हानिकारक है। यह निर्णय बोरलैंड इंटरनेशनल के परामर्श से किया गया था, जिन्हें डेल्फी ऑब्जेक्ट पास्कल में बाध्य विधि संदर्भों के साथ पिछला अनुभव था।
हमारा मानना है कि बाध्य विधि संदर्भ अनावश्यक हैं क्योंकि एक अन्य डिजाइन विकल्प, आंतरिक कक्षाएं , समान या बेहतर कार्यक्षमता प्रदान करती हैं। विशेष रूप से, आंतरिक कक्षाएं पूरी तरह से उपयोगकर्ता-इंटरफ़ेस इवेंट हैंडलिंग की आवश्यकताओं का समर्थन करती हैं, और इसका उपयोग कम से कम विंडोज फाउंडेशन कक्षाओं के रूप में व्यापक रूप से उपयोगकर्ता-इंटरफ़ेस एपीआई को लागू करने के लिए किया गया है।
हम मानते हैं कि बाध्य विधि संदर्भ हानिकारक हैं क्योंकि वे जावा प्रोग्रामिंग भाषा की सरलता और एपीआई की व्यापक वस्तु-उन्मुख चरित्र से अलग हैं। बाउंड मेथड रेफरेंस भी भाषा के सिंटैक्स और स्कूपिंग नियमों में अनियमितता का परिचय देता है। अंत में, वे वीएम प्रौद्योगिकियों में निवेश को कम कर देते हैं क्योंकि वीएम को अतिरिक्त और असमान प्रकार के संदर्भों और विधि लिंकेज को कुशलतापूर्वक संभालने की आवश्यकता होती है।
आप पढ़ी है यह :
डेलिगेट्स घटना-आधारित प्रणालियों में एक उपयोगी निर्माण है। अनिवार्य रूप से प्रतिनिधि वे ऑब्जेक्ट हैं जो किसी निर्दिष्ट ऑब्जेक्ट पर एक विधि प्रेषण को एन्कोड करते हैं। इस दस्तावेज़ से पता चलता है कि जावा भीतरी वर्ग कैसे इस तरह की समस्याओं के लिए एक अधिक सामान्य समाधान प्रदान करते हैं।
एक प्रतिनिधि क्या है? वास्तव में यह C ++ में उपयोग किए जाने वाले एक पॉइंटर टू मेम्बर फ़ंक्शन के समान है। लेकिन एक प्रतिनिधि को शामिल करने की विधि के साथ लक्ष्य वस्तु शामिल है। आदर्श रूप में यह कहना अच्छा होगा:
obj.registerHandler (ano.methodOne);
..और यह कि विधि मेथड किसी विशेष घटना को प्राप्त होने पर एओ पर कॉल किया जाएगा।
यह वही है जो डेलिगेट संरचना प्राप्त करती है।
जावा इनर क्लासेस
यह तर्क दिया गया है कि जावा यह कार्यक्षमता अनाम आंतरिक वर्गों के माध्यम से प्रदान करता है और इस प्रकार अतिरिक्त डेलिगेट निर्माण की आवश्यकता नहीं है।
obj.registerHandler(new Handler() {
public void handleIt(Event ev) {
methodOne(ev);
}
} );
पहली नज़र में यह सही लगता है लेकिन एक ही समय में एक उपद्रव। क्योंकि कई ईवेंट प्रोसेसिंग उदाहरणों के लिए डेलिगेट्स सिंटैक्स की सादगी बहुत आकर्षक है।
जनरल हैंडलर
हालाँकि, यदि इवेंट-आधारित प्रोग्रामिंग का उपयोग अधिक व्यापक रूप से किया जाता है, तो उदाहरण के लिए, सामान्य अतुल्यकालिक प्रोग्रामिंग वातावरण के एक हिस्से के रूप में, दांव पर अधिक है।
ऐसी सामान्य स्थिति में, केवल लक्ष्य विधि और लक्ष्य वस्तु उदाहरण को शामिल करना पर्याप्त नहीं है। सामान्य तौर पर आवश्यक अन्य पैरामीटर हो सकते हैं, जो कि घटना के हैंडलर के पंजीकृत होने पर संदर्भ में निर्धारित किए जाते हैं।
इस अधिक सामान्य स्थिति में, जावा दृष्टिकोण एक बहुत ही सुंदर समाधान प्रदान कर सकता है, खासकर जब अंतिम चर के उपयोग के साथ जोड़ा जाता है:
void processState(final T1 p1, final T2 dispatch) {
final int a1 = someCalculation();
m_obj.registerHandler(new Handler() {
public void handleIt(Event ev) {
dispatch.methodOne(a1, ev, p1);
}
} );
}
फाइनल * फाइनल * फाइनल
आपका ध्यान गया?
ध्यान दें कि अंतिम चर अनाम वर्ग विधि परिभाषाओं के भीतर से सुलभ हैं। इस कोड का ध्यान से अध्ययन करें ताकि आप समझ सकें। यह संभावित रूप से एक बहुत शक्तिशाली तकनीक है। उदाहरण के लिए, मिनीडॉम में और अधिक सामान्य स्थितियों में हैंडलर को पंजीकृत करते समय अच्छे प्रभाव के लिए इसका उपयोग किया जा सकता है।
इसके विपरीत, डेलिगेट निर्माण इस अधिक सामान्य आवश्यकता के लिए एक समाधान प्रदान नहीं करता है, और जैसे कि एक मुहावरे के रूप में खारिज किया जाना चाहिए, जिस पर डिजाइन आधारित हो सकते हैं।
मुझे पता है कि यह पोस्ट पुरानी है, लेकिन जावा 8 में लैम्ब्डा, और एक कार्यात्मक इंटरफ़ेस की अवधारणा को जोड़ा गया है, जो कि केवल अन्य विधि के साथ कोई भी इंटरफ़ेस है। साथ में ये C # प्रतिनिधियों को समान कार्यक्षमता प्रदान करते हैं। अधिक जानकारी के लिए यहां देखें, या सिर्फ Google Java Lambdas। http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
नहीं, लेकिन वे परदे के पीछे और प्रतिबिंब का उपयोग करने योग्य हैं:
public static class TestClass {
public String knockKnock() {
return "who's there?";
}
}
private final TestClass testInstance = new TestClass();
@Test public void
can_delegate_a_single_method_interface_to_an_instance() throws Exception {
Delegator<TestClass, Callable<String>> knockKnockDelegator = Delegator.ofMethod("knockKnock")
.of(TestClass.class)
.to(Callable.class);
Callable<String> callable = knockKnockDelegator.delegateTo(testInstance);
assertThat(callable.call(), is("who's there?"));
}
इस मुहावरे के बारे में अच्छी बात यह है कि आप सत्यापित कर सकते हैं कि प्रत्यायोजित विधि मौजूद है, और आवश्यक हस्ताक्षर हैं, उस बिंदु पर जहां आप प्रतिनिधि बनाते हैं (हालांकि संकलन-समय पर नहीं, दुर्भाग्य से, हालांकि FindBugs प्लग-इन हो सकता है यहां मदद करें), फिर विभिन्न उदाहरणों को सौंपने के लिए इसे सुरक्षित रूप से उपयोग करें।
अधिक परीक्षणों और कार्यान्वयन के लिए जीथब पर कार्ग कोड देखें ।
मैंने प्रतिबिंब का उपयोग करके जावा में कॉलबैक / प्रतिनिधि समर्थन लागू किया है। विवरण और कार्य स्रोत मेरी वेबसाइट पर उपलब्ध हैं ।
WithParms नाम के नेस्टेड क्लास के साथ कॉलबैक नाम का एक सिद्धांत वर्ग है। जिस API को कॉलबैक की आवश्यकता है, वह एक कॉलबैक ऑब्जेक्ट को एक पैरामीटर के रूप में लेगा और यदि neccessary है, तो एक कॉलबैक .Parms विधि चर के रूप में बनाएं। चूंकि इस ऑब्जेक्ट के बहुत सारे अनुप्रयोग पुनरावर्ती होंगे, यह बहुत सफाई से काम करता है।
प्रदर्शन के साथ मेरे लिए अभी भी एक उच्च प्राथमिकता है, मैं हर आह्वान के लिए मापदंडों को रखने के लिए एक भगोड़ा वस्तु सरणी बनाने के लिए आवश्यक नहीं होना चाहता था - आखिरकार एक बड़ी डेटा संरचना में हजारों तत्व हो सकते हैं, और एक संदेश प्रसंस्करण में परिदृश्य हम अंत में हजारों डेटा संरचनाओं के प्रसंस्करण को समाप्त कर सकते हैं।
थ्रेडसेफ़ होने के लिए, पैरामीटर सरणी को एपीआई विधि के प्रत्येक आह्वान के लिए विशिष्ट रूप से मौजूद होना चाहिए, और दक्षता के लिए कॉलबैक के प्रत्येक आह्वान के लिए उसी का उपयोग किया जाना चाहिए; मुझे दूसरी वस्तु की आवश्यकता थी, जो मंगलाचरण के लिए एक पैरामीटर सरणी के साथ कॉलबैक को बांधने के लिए बनाने के लिए सस्ता होगा। लेकिन, कुछ परिदृश्यों में, इनवॉकर में पहले से ही अन्य कारणों के लिए पैरामीटर सरणी होगी। इन दो कारणों से, पैरामीटर सरणी कॉलबैक ऑब्जेक्ट में नहीं है। इसके अलावा आह्वान का विकल्प (मापदंडों को एक सरणी के रूप में या व्यक्तिगत वस्तुओं के रूप में पारित करना) एपीआई के हाथों में होता है जो कॉलबैक का उपयोग करके सक्षम बनाता है जो भी मंगलाचरण का उपयोग करता है वह अपने आंतरिक कामकाज के लिए सबसे उपयुक्त है।
WithParms नेस्टेड क्लास है, तब, वैकल्पिक है और दो उद्देश्यों को पूरा करता है, इसमें कॉलबैक इनवोकेशन के लिए आवश्यक पैरामीटर ऑब्जेक्ट सरणी है, और यह 10 ओवरलोडेड इनवोक () विधियों (1 से 10 मापदंडों के साथ) प्रदान करता है जो पैरामीटर सरणी को लोड करते हैं और फिर कॉलबैक लक्ष्य प्राप्त करें।
निर्देशिका ट्री में फ़ाइलों को संसाधित करने के लिए कॉलबैक का उपयोग करके एक उदाहरण निम्न है। यह एक प्रारंभिक सत्यापन पास है जो सिर्फ फाइलों को प्रोसेस करने के लिए गिनता है और यह सुनिश्चित करता है कि कोई पूर्व निर्धारित अधिकतम आकार से अधिक न हो। इस मामले में हम बस एपीआई इनवोकेशन के साथ कॉलबैक इनलाइन बनाते हैं। हालाँकि, हम लक्ष्य पद्धति को एक स्थिर मूल्य के रूप में दर्शाते हैं ताकि प्रतिबिंब हर बार न हो।
static private final Method COUNT =Callback.getMethod(Xxx.class,"callback_count",true,File.class,File.class);
...
IoUtil.processDirectory(root,new Callback(this,COUNT),selector);
...
private void callback_count(File dir, File fil) {
if(fil!=null) { // file is null for processing a directory
fileTotal++;
if(fil.length()>fileSizeLimit) {
throw new Abort("Failed","File size exceeds maximum of "+TextUtil.formatNumber(fileSizeLimit)+" bytes: "+fil);
}
}
progress("Counting",dir,fileTotal);
}
IoUtil.processDirectory ():
/**
* Process a directory using callbacks. To interrupt, the callback must throw an (unchecked) exception.
* Subdirectories are processed only if the selector is null or selects the directories, and are done
* after the files in any given directory. When the callback is invoked for a directory, the file
* argument is null;
* <p>
* The callback signature is:
* <pre> void callback(File dir, File ent);</pre>
* <p>
* @return The number of files processed.
*/
static public int processDirectory(File dir, Callback cbk, FileSelector sel) {
return _processDirectory(dir,new Callback.WithParms(cbk,2),sel);
}
static private int _processDirectory(File dir, Callback.WithParms cbk, FileSelector sel) {
int cnt=0;
if(!dir.isDirectory()) {
if(sel==null || sel.accept(dir)) { cbk.invoke(dir.getParent(),dir); cnt++; }
}
else {
cbk.invoke(dir,(Object[])null);
File[] lst=(sel==null ? dir.listFiles() : dir.listFiles(sel));
if(lst!=null) {
for(int xa=0; xa<lst.length; xa++) {
File ent=lst[xa];
if(!ent.isDirectory()) {
cbk.invoke(dir,ent);
lst[xa]=null;
cnt++;
}
}
for(int xa=0; xa<lst.length; xa++) {
File ent=lst[xa];
if(ent!=null) { cnt+=_processDirectory(ent,cbk,sel); }
}
}
}
return cnt;
}
यह उदाहरण इस दृष्टिकोण की सुंदरता को दिखाता है - एप्लिकेशन विशिष्ट लॉजिक कॉलबैक में सारगर्भित है, और एक डायरेक्टरी ट्री घूमने के नशे में पूरी तरह से पुन: प्रयोज्य स्थैतिक उपयोगिता विधि में अच्छी तरह से टक गया है। और हमें हर नए उपयोग के लिए इंटरफ़ेस को परिभाषित करने और लागू करने की कीमत बार-बार चुकानी नहीं पड़ती है। बेशक, तर्क के लिए , लेकिन व्यवहार में मैं नहीं मिला है यह कॉलबैक परिभाषा सही पाने के लिए एक समस्या हो - एक अंतरफलक यह (यह लागू है बस दस्तावेज नहीं) कहीं अधिक क्या लागू करने के बारे में स्पष्ट है।
एक इंटरफ़ेस को परिभाषित करना और लागू करना वास्तव में इतना बुरा नहीं है (जब तक कि आप एप्लेट वितरित नहीं कर रहे हैं, जैसा कि मैं हूं, जहां वास्तव में अतिरिक्त कक्षाएं बनाने से बचा जाता है) मायने रखता है, लेकिन जहां यह वास्तव में चमकता है जब आपके पास एक एकल वर्ग में कई कॉलबैक होते हैं। न केवल उन्हें एक अलग आंतरिक वर्ग में एक अलग धक्का दिया जा रहा है जो तैनात अनुप्रयोग में उपरि से जोड़ा गया है, लेकिन यह प्रोग्राम के लिए सर्वथा थकाऊ है और यह सब बॉयलर-प्लेट कोड वास्तव में सिर्फ "शोर" है।
हां और नहीं, लेकिन जावा में प्रतिनिधि पैटर्न इस तरह से सोचा जा सकता है। यह वीडियो ट्यूटोरियल गतिविधि - अंशों के बीच डेटा विनिमय के बारे में है, और इसमें इंटरफेस का उपयोग करते हुए प्रतिनिधि सॉर्ट पैटर्न का बहुत सार है।
इसमें delegate
C # के रूप में एक स्पष्ट कीवर्ड नहीं है , लेकिन आप एक कार्यात्मक इंटरफ़ेस (यानी बिल्कुल एक विधि के साथ कोई भी इंटरफ़ेस) और लंबोदा का उपयोग करके जावा 8 में समान हासिल कर सकते हैं:
private interface SingleFunc {
void printMe();
}
public static void main(String[] args) {
SingleFunc sf = () -> {
System.out.println("Hello, I am a simple single func.");
};
SingleFunc sfComplex = () -> {
System.out.println("Hello, I am a COMPLEX single func.");
};
delegate(sf);
delegate(sfComplex);
}
private static void delegate(SingleFunc f) {
f.printMe();
}
प्रत्येक प्रकार की नई वस्तु SingleFunc
को लागू करना चाहिए printMe()
, इसलिए विधि delegate(SingleFunc)
को कॉल करने के लिए इसे किसी अन्य विधि (जैसे ) में पास करना सुरक्षित है printMe()
।
हालांकि यह लगभग साफ सुथरा नहीं है, लेकिन आप जावा प्रॉक्सी का उपयोग करके C # प्रतिनिधियों की तरह कुछ लागू कर सकते हैं ।
नहीं, लेकिन इसका व्यवहार समान है, आंतरिक रूप से।
C # प्रतिनिधियों में एक अलग प्रविष्टि बिंदु बनाने के लिए उपयोग किया जाता है और वे फ़ंक्शन पॉइंटर की तरह बहुत काम करते हैं।
जावा में फंक्शन पॉइंटर (ऊपरी नज़र में) जैसी कोई चीज़ नहीं होती है, लेकिन आंतरिक रूप से जावा को अपने उद्देश्यों को प्राप्त करने के लिए उसी चीज़ को करने की आवश्यकता होती है।
उदाहरण के लिए, जावा में थ्रेड बनाने के लिए थ्रेड को एक्सटेंड करने या रननेबल को लागू करने के लिए एक क्लास की आवश्यकता होती है, क्योंकि एक क्लास ऑब्जेक्ट वेरिएबल का उपयोग मेमोरी मेमोरी पॉइंटर में किया जा सकता है।
नहीं, जावा में वह अद्भुत विशेषता नहीं है। लेकिन आप इसे प्रेक्षक पैटर्न का उपयोग करके मैन्युअल रूप से बना सकते हैं। यहाँ एक उदाहरण है: जावा में C # प्रतिनिधि लिखें
वर्णित कोड C # प्रतिनिधियों के कई फायदे प्रदान करता है। विधियों, या तो स्थिर या गतिशील, एक समान तरीके से इलाज किया जा सकता है। परावर्तन के माध्यम से कॉलिंग विधियों में जटिलता कम हो जाती है और उपयोगकर्ता कोड में अतिरिक्त कक्षाओं की आवश्यकता नहीं होती है। ध्यान दें कि हम एक वैकल्पिक सुविधा संस्करण का आह्वान कर रहे हैं, जहां एक ऑब्जेक्ट के बिना एक पैरामीटर के साथ एक विधि को बुलाया जा सकता है। जेसी कोड:
class Class1 {
public void show(String s) { System.out.println(s); }
}
class Class2 {
public void display(String s) { System.out.println(s); }
}
// allows static method as well
class Class3 {
public static void staticDisplay(String s) { System.out.println(s); }
}
public class TestDelegate {
public static final Class[] OUTPUT_ARGS = { String.class };
public final Delegator DO_SHOW = new Delegator(OUTPUT_ARGS,Void.TYPE);
public void main(String[] args) {
Delegate[] items = new Delegate[3];
items[0] = DO_SHOW .build(new Class1(),"show,);
items[1] = DO_SHOW.build (new Class2(),"display");
items[2] = DO_SHOW.build(Class3.class, "staticDisplay");
for(int i = 0; i < items.length; i++) {
items[i].invoke("Hello World");
}
}
}
जावा के पास प्रतिनिधि नहीं हैं और इस पर गर्व है :)। यहाँ मैंने जो कुछ भी पढ़ा उससे मुझे नकली प्रतिनिधियों को 2 तरीके मिले: 1. प्रतिबिंब; 2. भीतरी वर्ग
प्रतिबिंब नारे हैं! इनर क्लास सरलतम उपयोग-मामले को कवर नहीं करता है: सॉर्ट फ़ंक्शन। विवरण में नहीं जाना चाहते हैं, लेकिन मूल रूप से आंतरिक वर्ग के साथ समाधान पूर्णांक की एक सरणी के लिए एक आवरण वर्ग बनाने के लिए आरोही क्रम में सॉर्ट किया जाता है और पूर्णांक में सरणी के पूर्णांक के लिए एक वर्ग बनाया जाता है।