क्या सही तरीके होने की तुलना में इंटरफ़ेस में अधिक है


159

तो चलो मैं यह इंटरफ़ेस है:

public interface IBox
{
   public void setSize(int size);
   public int getSize();
   public int getArea();
  //...and so on
}

और मेरे पास एक वर्ग है जो इसे लागू करता है:

public class Rectangle implements IBox
{
   private int size;
   //Methods here
}

अगर मैं इंटरफ़ेस IBox का उपयोग करना चाहता था, तो मैं वास्तव में इसका एक उदाहरण नहीं बना सकता, जिस तरह से:

public static void main(String args[])
{
    Ibox myBox=new Ibox();
}

सही? इसलिए मुझे वास्तव में ऐसा करना होगा:

public static void main(String args[])
{
    Rectangle myBox=new Rectangle();
}

यदि यह सच है, तो इंटरफेस का एकमात्र उद्देश्य यह सुनिश्चित करना है कि एक इंटरफ़ेस को जिस वर्ग ने लागू किया है, उसे सही तरीके मिले हैं? या इंटरफेस का कोई अन्य उपयोग है?


2
याद रखें, इंटरफेस जावा के लिए विशिष्ट नहीं हैं। सभी OOP भाषाओं, कुछ या किसी अन्य रूप में उन्हें है, हालांकि नहीं हमेशा की तरह स्पष्ट रूप से जावा के रूप में परिभाषित किया।
हर्म्स 21

2
तकनीकी रूप से, सभी दृढ़ता से टाइप की गई OOP भाषाएँ उन्हें किसी न किसी रूप में होती हैं। अनकैप्ड, या डक टाइप, भाषाओं में एक जैसी अवधारणा नहीं है।
जारेड

1
@Jared आप स्थिर टाइपिंग के साथ मजबूत टाइपिंग भ्रमित नहीं कर रहे हैं, और गतिशील रूप से टाइप किया के साथ "untyped"?
eljenso

बहुरूपता भी इंटरफेस के माध्यम से पूरा किया जा सकता। इस पृष्ठ के अंतिम भाग की जाँच करें codenuggets.com/2014/06/20/java-interface
Jeff

जवाबों:


143

इंटरफेस आपके कोड को अधिक लचीला बनाने का एक तरीका है। आप यह क्या करते हैं:

Ibox myBox=new Rectangle();

फिर, बाद में, यदि आप तय करते हैं कि आप एक अलग तरह के बॉक्स का उपयोग करना चाहते हैं (हो सकता है कि एक और पुस्तकालय हो, एक बेहतर तरह के बॉक्स के साथ), आप अपना कोड इस पर स्विच कर दें:

Ibox myBox=new OtherKindOfBox();

एक बार जब आपको इसकी आदत हो जाती है, तो आप पाएंगे कि यह काम करने का एक शानदार तरीका है।

एक और कारण है, उदाहरण के लिए, यदि आप बक्से की एक सूची बनाना चाहते हैं और प्रत्येक पर कुछ ऑपरेशन करना चाहते हैं, लेकिन आप चाहते हैं कि सूची में विभिन्न प्रकार के बक्से हों। प्रत्येक बॉक्स पर आप कर सकते हैं:

myBox.close()

(मान लें कि IBox में एक निकट () विधि है, भले ही myBox का वास्तविक वर्ग इस बात पर निर्भर करता है कि आप किस बॉक्स में चलना चाहते हैं।


54
इस उत्तर में कुछ भी नहीं है जो जावा इंटरफेस के लिए अनन्य है । वही अमूर्त वर्गों, या यहां तक ​​कि ठोस लोगों के लिए समान रूप से लागू होता है। मैं एक अच्छे उत्तर की अपेक्षा करूंगा कि वह कई इंटरफेस को लागू करने की क्षमता का उल्लेख करे और कब / क्यों उपयोगी होगा।
Rogério

16
इसका उत्तर के रूप में चयन कैसे हुआ? यह एक संक्षिप्त विवरण है कि बहुरूपता क्यों उपयोगी है, लेकिन जैसा कि ऊपर पोस्टर में कहा गया है, मैं कई इंटरफेस के बेहतर स्पष्टीकरण की उम्मीद करूंगा और इससे भी महत्वपूर्ण बात यह है कि जब यह एक इंटरफ़ेस बनाम एक सार वर्ग का उपयोग करने के लिए उपयुक्त है।
trevorkavanaugh

2
यह बहुत कम करने के लिए व्याख्याओं और सब कुछ बहुरूपता की मूल बातें के साथ क्या करना है
A-Developer-Has-No-Name

123

इंटरफेस को उपयोगी बनाने वाला तथ्य यह नहीं है कि "आप अपने दिमाग को बदल सकते हैं और बाद में एक अलग कार्यान्वयन का उपयोग कर सकते हैं और केवल उसी स्थान को बदलना होगा जहां ऑब्जेक्ट बनाया गया है"। यह एक गैर-मुद्दा है।

असली बिंदु पहले से ही नाम में है: वे एक इंटरफ़ेस को परिभाषित करते हैं जिसे कोई भी उस इंटरफ़ेस पर संचालित सभी कोड का उपयोग करने के लिए लागू कर सकता है। सबसे अच्छा उदाहरण वह है java.util.Collectionsजो सभी प्रकार के उपयोगी तरीके प्रदान करता है जो विशेष रूप से इंटरफेस पर संचालित होते हैं, जैसे कि sort()या इसके reverse()लिए List। यहाँ पर मुद्दा यह है कि इस कोड का उपयोग अब किसी भी वर्ग को सॉर्ट या रिवर्स करने के लिए किया जा सकता है जो Listइंटरफेस को लागू करता है - न केवल ArrayListऔर LinkedList, बल्कि उन कक्षाओं को भी जो आप खुद लिखते हैं, जिन्हें उन लोगों के लिए लागू किया जा सकता है जिन्होंने java.util.Collectionsकभी कल्पना नहीं की थी।

उसी तरह, आप ऐसे कोड लिख सकते हैं जो जाने-माने इंटरफेस पर काम करते हैं, या आपके द्वारा परिभाषित किए गए इंटरफेस, और अन्य लोग आपके कोड का उपयोग किए बिना आपसे अपनी कक्षाओं का समर्थन करने के लिए कह सकते हैं।

कॉलबैक के लिए इंटरफेस का एक और सामान्य उपयोग है। उदाहरण के लिए, java.swing.table.TableCellRenderer , जो आपको यह प्रभावित करने की अनुमति देता है कि किसी निश्चित कॉलम में स्विंग तालिका डेटा को कैसे प्रदर्शित करती है। आप उस इंटरफ़ेस को कार्यान्वित करते हैं, एक उदाहरण को पास करते हैं JTable, और कुछ बिंदु पर तालिका के प्रतिपादन के दौरान, आपके कोड को अपना सामान करने के लिए बुलाया जाएगा।


9
जब आप जावा पैकेज वर्गों से उदाहरण भी दिए है कि एक बहुत अच्छी जवाब, मैं इसे की तरह है ...
ओवैस कुरैशी

1
मुझे पसंद आयाyou can write code that operates on well-known interfaces, or interfaces you define
मनीष कुमार

एक पल रुकिए ... जो इंटरफेस उपयोगी बनाता है वह [आपके द्वारा किसी भी कार्यान्वयन का उपयोग करने की क्षमता] नहीं है, बल्कि [आप की तरह किसी भी कार्यान्वयन का उपयोग करने की क्षमता] है? हालांकि विपरीत मामले का उल्लेख करना बहुत अच्छी बात है।
पॉवर्सलेव

4
@ पॉवर्सलेव: इसे और अधिक पसंद करें जैसे कि इंटरफेस को उपयोगी बनाने के लिए [कोड लिखने की क्षमता नहीं है जहां आपको अशुद्धता बदलते समय केवल एक पंक्ति को बदलना पड़ता है] बल्कि [कोड लिखने की क्षमता भी है जहां आप कार्यान्वयन को निर्दिष्ट नहीं करते हैं। सब]।
माइकल Borgwardt

@MichaelBorgwardt लगता है कि बेहतर तरीका है। :) स्पष्टीकरण देने के लिए धन्यवाद!
पॉवर्सलेव

119

मेरे द्वारा पढ़े गए कई उपयोगों में से एक यह है कि जावा में कई-विरासत-उपयोग-इंटरफेस के बिना इसकी मुश्किल कहां है:

class Animal
{
void walk() { } 
....
.... //other methods and finally
void chew() { } //concentrate on this
} 

अब, एक ऐसे मामले की कल्पना करें जहां:

class Reptile extends Animal 
{ 
//reptile specific code here
} //not a problem here

परंतु,

class Bird extends Animal
{
...... //other Bird specific code
} //now Birds cannot chew so this would a problem in the sense Bird classes can also call chew() method which is unwanted

बेहतर डिजाइन होगा:

class Animal
{
void walk() { } 
....
.... //other methods 
} 

पशु के पास चबाने () विधि नहीं है और इसके बजाय एक इंटरफ़ेस में रखा गया है:

interface Chewable {
void chew();
}

और सरीसृप वर्ग इसे लागू करते हैं और पक्षी नहीं (क्योंकि पक्षी चबा नहीं सकते हैं):

class Reptile extends Animal implements Chewable { } 

और पक्षियों का आवेश बस:

class Bird extends Animal { }

6
@CHEBURASHKA और बुरा नामकरण। यदि Reptile"चबाता है", तो स्वयं "चबाने योग्य" नहीं है। (कभी-कभी) नामकरण का कन्वेंशन जो कुछ भी होता है उसे केवल उसी स्थान पर लागू किया जाना चाहिए जहां यह सही अर्थ बनाता है। इंटरफ़ेस का नामकरण Predatorयहां अधिक उपयुक्त होगा।
पॉवर्सलेव

6
@ पॉवर्सलेव यह सही है इमो, एक सरीसृप "चबाने में सक्षम" / "चबाने योग्य" है। एक बाज एक शिकारी है, लेकिन अभी भी चबाने में सक्षम नहीं है ... सिर्फ नाइटपैकिंग लेकिन "चबाने योग्य" को इंटरफेस के प्रलेखन में बेहतर परिभाषित किया जा सकता है।
Madmenyo

शानदार .. बहुत अच्छा समझाया। धन्यवाद..!
गुरुसिंघ

1
मुझे नहीं मिला। यह सब ऐसा लगता है कि इंटरफेस यह सुनिश्चित करने का एक अच्छा तरीका है कि आप प्रत्येक कक्षा में उन्हीं तरीकों को लागू करें जो लागू होते हैं, (जैसे। इसलिए यह मुझे रन () और डॉग क्लास के साथ बर्ड क्लास की बेवकूफ पसंद करने से रोकता है। runn के साथ () - वे सभी समान हैं)। लेकिन ध्यान देने और मेरी कक्षाओं को बनाने में एक ही विधि प्रारूप / संरचनाएं हैं, क्या मैं एक ही चीज हासिल नहीं कर सकता? वास्तव में इंटरफेस की तरह लगता है कि प्रोग्रामर को भुलक्कड़ नहीं होना चाहिए। इसके अलावा, इंटरफेस मुझे समय बचाने के लिए प्रतीत नहीं होते हैं; मुझे अभी भी प्रत्येक वर्ग में उस विधि को परिभाषित करना होगा जो इसे लागू करती है।
एलेक्स जी

@AlexG - मैंने बताया कि कई उपयोगों में से एक है यार :) और भी हैं, हमने एक साधारण अंदाज़ में सवाल का जवाब देने के लिए सतह को मुश्किल से खुरच दिया था!
पेशवाई

47

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

public void scale(IBox b, int i) {
   b.setSize(b.getSize() * i);
}

scaleविधि को कॉल करते समय , आप किसी भी मूल्य को प्रदान कर सकते हैं जो एक प्रकार का है जो IBoxइंटरफ़ेस को लागू करता है। दूसरे शब्दों में, यदि Rectangleऔर Squareदोनों लागू होते हैं IBox, तो आप Rectangleया तो एक या Squareजहां भी IBoxअपेक्षित हो, प्रदान कर सकते हैं ।


8
यदि मैं पहले ही जावा में उपवर्ग और विधि ओवरराइडिंग के साथ इसे प्राप्त कर सकता हूं, तो इंटरफ़ेस पॉलीमॉर्फिज़्म का उद्देश्य क्यों है?
eljenso

1
यह एक ही बात है, सिवाय इसके कि इंटरफेस किसी भी कार्यान्वयन को छोड़ना होगा। इसलिए कक्षाएं एक से अधिक इंटरफ़ेस लागू कर सकती हैं।
Apocalisp

4
अरे, मैंने कभी नहीं कहा कि जावा में किसी भी तरह की वैचारिक अखंडता थी। टाइप सबस्टेशन सब उपप्रयोग का उद्देश्य है। जावा में एक से अधिक सबटाइपिंग तंत्र होते हैं, जिनमें से कोई भी विशेष रूप से अच्छा नहीं होता है।
Apocalisp

1
मैंने वैचारिक अखंडता के बारे में भी कभी कुछ नहीं कहा। लेकिन चलो आगे बढ़ते हैं। यदि आप अपनी विधि से हर IBox को स्केल कर सकते हैं, तो क्या यह IBox: IBox.scale (int) पर घोषित ऑपरेशन नहीं होना चाहिए?
एलजेन्सो

1
हम Integer को IBox से नहीं जोड़ना चाहेंगे, इसीलिए हम इसे Integer पर एक विधि नहीं बनाते हैं। और एक अंतरफलक पर तरीकों की संख्या को यह स्पष्टता के अमूर्तता और सामंजस्य द्वारा तय किया जाता है, न कि इसे लागू करने के लिए कितना बोझिल होगा। वैसे भी, आपके उत्तर के लिए धन्यवाद एपो।
eljenso

33

Interfaces बहुरूपित भाषाओं को बहुरूपता का समर्थन करने की अनुमति देते हैं। ऑब्जेक्ट ओरिएंटेड प्यूरिस्ट का आग्रह होगा कि एक भाषा पूरी तरह से चित्रित ऑब्जेक्ट ओरिएंटेड भाषा होने के लिए विरासत, एनकैप्सुलेशन, प्रतिरूपकता और बहुरूपता प्रदान करे। डायनामिक रूप से टाइप किए गए - या बतख टाइप किए गए - भाषाएं (जैसे स्मॉलटाकल), पॉलीमॉर्फिज़्म तुच्छ है; हालाँकि, वैधानिक रूप से टाइप की गई भाषाओं में (जैसे जावा या C #), बहुरूपता तुच्छता से बहुत दूर है (वास्तव में, सतह पर यह मजबूत टाइपिंग की धारणा के साथ है।)

मुझे प्रदर्शित करें:

डायनामिक रूप से टाइप की गई (या डक टाइप की गई) भाषा (जैसे स्मॉलटाकल), सभी चर वस्तुओं के संदर्भ में हैं (कुछ कम नहीं और कुछ अधिक नहीं।) इसलिए, स्मालटाक में, मैं यह कर सकता हूं:

|anAnimal|    
anAnimal := Pig new.
anAnimal makeNoise.

anAnimal := Cow new.
anAnimal makeNoise.

वह कोड:

  1. एक स्थानीय चर घोषित किया जाता है जिसे अनिमल कहा जाता है (ध्यान दें कि हम चर के प्रकार को निर्दिष्ट नहीं करते हैं - सभी चर एक वस्तु के संदर्भ हैं, कोई अधिक और कोई कम नहीं।)
  2. "पिग" नामक वर्ग का एक नया उदाहरण बनाता है
  3. चर के उस नए उदाहरण को चर पर निरुपित करता है।
  4. makeNoiseसुअर को संदेश भेजता है ।
  5. एक गाय का उपयोग करके पूरी बात दोहराता है, लेकिन इसे सुअर के समान सटीक चर में असाइन करना।

एक ही जावा कोड कुछ इस तरह दिखाई देगा (यह धारणा बनाते हुए कि बतख और गाय पशु के उपवर्ग हैं:

Animal anAnimal = new Pig();
duck.makeNoise();

anAnimal = new Cow();
cow.makeNoise();

यह सब अच्छी तरह से और अच्छा है, जब तक हम क्लास वेजीटेबल का परिचय नहीं देते। सब्जियों में जानवरों के समान व्यवहार होता है, लेकिन सभी नहीं। उदाहरण के लिए, पशु और वनस्पति दोनों ही विकसित करने में सक्षम हो सकते हैं, लेकिन स्पष्ट रूप से सब्जियां शोर नहीं करती हैं और जानवरों को काटा नहीं जा सकता है।

स्मालटाक में, हम इसे लिख सकते हैं:

|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.

aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.

यह स्मॉलटाकल में पूरी तरह से अच्छी तरह से काम करता है क्योंकि यह डक-टाइप्ड है (यदि यह बत्तख की तरह चलता है, और बत्तख की तरह चुटकी लेता है - यह बत्तख है।) इस मामले में, जब किसी वस्तु को संदेश भेजा जाता है, तो एक लुकअप किया जाता है। रिसीवर की विधि सूची, और यदि एक मिलान विधि पाई जाती है, तो उसे कहा जाता है। यदि नहीं, तो किसी प्रकार के NoSuchMethodError अपवाद को फेंक दिया जाता है - लेकिन यह सब रनटाइम पर किया जाता है।

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

आने वाले इंटरफेस के साथ ...

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

List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());

for(Growable g : list) {
   g.grow();
}

और यह हमें ऐसा करने की अनुमति देता है, जिससे पशु शोर कर सके:

List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
  a.makeNoise();
}

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

|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.

सांख्यिकीय रूप से टाइप की गई भाषाएं "अनुबंध द्वारा प्रोग्रामिंग" को बहुत बेहतर प्रदान करती हैं, क्योंकि वे संकलन-समय पर नीचे दो प्रकार की त्रुटि पकड़ लेंगे:

// Compiler error: Corn cannot be cast to Animal.
Animal farmObject = new Corn();  
farmObject makeNoise();

-

// Compiler error: Animal doesn't have the harvest message.
Animal farmObject = new Cow();
farmObject.harvest(); 

इसलिए .... संक्षेप में:

  1. इंटरफ़ेस कार्यान्वयन आपको यह निर्दिष्ट करने की अनुमति देता है कि ऑब्जेक्ट किस प्रकार की चीजें कर सकते हैं (इंटरैक्शन) और क्लास इनहेरिटेंस आपको यह निर्दिष्ट करने देता है कि चीजों को कैसे किया जाना चाहिए (कार्यान्वयन)।

  2. संकलक प्रकार की जाँच के त्याग के बिना, इंटरफेस हमें "सच्चे" बहुरूपता के कई लाभ देते हैं।


2
यह एक अन्य प्रश्न के मेरे उत्तर का पाठ है: stackoverflow.com/questions/379282/… । लेकिन, वे संबंधित उत्तर हैं।
जारेड

2
तो क्या मैं पूछ सकता हूं कि कैसे एक बतख टाइप की गई भाषा Animal.water () के बीच अंतर करती है (जो कि, विवेकशील किसान कहते थे कि इसमें रिसाव होता है) और Plant.water () जो वह पानी के पौधों का उपयोग करता है। अस्पष्टता ही शत्रु है। अस्पष्टता को दूर करने के लिए आवश्यक किसी भी प्रकार की मौखिकता स्वीकार्य IMO है।
बिल के

1
Yep..ambiguity बतख टाइप भाषाओं के साथ खेल का नाम है। बतख टाइप की हुई भाषा में पेशेवर रूप से काम करते समय, सदस्यों (विधियों और चर) को ऐसे नामों से देखना असामान्य नहीं है जिनकी लंबाई 50-100 अक्षर है।
जेरेड

1
बतख टाइप की गई भाषाओं का एक और बड़ा पहलू स्थिर विश्लेषण के आधार पर प्रोग्रामेटिक रीफैक्टरिंग करने में असमर्थता है - अपने प्रिंटस्ट्रिंग विधि के सभी कॉलर्स की सूची के लिए एक स्मालटाक छवि पूछने की कोशिश करें ... आपको सभी प्रिंटस्ट्रीमिंग विधियों के सभी कॉलर्स की सूची मिल जाएगी। ...
जारेड

... क्योंकि ऑटोमोबाइल # प्रिंटस्ट्रिंग के कॉलर को प्रोग्रामर को नियर ऑर्थरबिट # प्रिंटस्ट्रिंग के कॉलर से अलग नहीं किया जा सकता है।
जारेड

9

आम तौर पर इंटरफेस आपको इंटरफ़ेस का उपयोग करना चाहिए (जैसा कि नाम यह कहता है; ;-))। नमूना


public void foo(List l) {
   ... do something
}

अब आपका फ़ंक्शन s, s, ... को केवल एक प्रकार fooस्वीकार करता है ।ArrayListLinkedList

जावा में सबसे महत्वपूर्ण बात यह है कि आप कई इंटरफेस लागू कर सकते हैं लेकिन आप केवल एक वर्ग का विस्तार कर सकते हैं! नमूना:


class Test extends Foo implements Comparable, Serializable, Formattable {
...
}
संभव है लेकिन

class Test extends Foo, Bar, Buz {
...
}
नहीं है!

ऊपर आपका कोड भी हो सकता है IBox myBox = new Rectangle();:। अब महत्वपूर्ण बात यह है, कि myBox में केवल IBox से विधियाँ / फ़ील्ड शामिल हैं, न कि (संभवतः मौजूदा) अन्य विधियों से Rectangle


1
क्या 'सूची' को एक इंटरफ़ेस सदस्य माना जाता है?
क्लिक करें

1
सूची जावा संग्रह पुस्तकालय में एक इंटरफ़ेस है।
21

सूची मानक जावा लाइब्रेरी ( java.sun.com/javase/6/docs/api/java/util/List.html ) में एक इंटरफ़ेस है । वह अपनी बात को स्पष्ट करने के लिए इसका उपयोग कर रहा है।
माइकल मायर्स

6

मुझे लगता है कि आप समझते हैं कि सब कुछ इंटरफेस करते हैं, लेकिन आप अभी तक उन स्थितियों की कल्पना नहीं कर रहे हैं जिनमें इंटरफ़ेस उपयोगी है।

यदि आप किसी ऑब्जेक्ट को एक संकीर्ण दायरे (उदाहरण के लिए, एक विधि कॉल के भीतर) में सभी का उपयोग कर रहे हैं और जारी कर रहे हैं, तो एक इंटरफ़ेस वास्तव में कुछ भी नहीं करता है। जैसा आपने उल्लेख किया है, कंक्रीट वर्ग जाना जाता है।

जहां इंटरफेस उपयोगी होते हैं, जब एक वस्तु को एक जगह बनाने की जरूरत होती है और एक कॉलर को लौटा दिया जाता है जो कार्यान्वयन विवरणों की परवाह नहीं कर सकता है। आइए अपने IBox उदाहरण को एक आकृति में बदलें। अब हमारे पास आकार के कार्यान्वयन हो सकते हैं जैसे आयत, वृत्त, त्रिभुज, इत्यादि, प्रत्येक कंक्रीट वर्ग के लिए getArea () और getSize () विधियों का कार्यान्वयन पूरी तरह से अलग होगा।

अब आप विभिन्न प्रकार के createShape (params) विधियों के साथ एक कारखाने का उपयोग कर सकते हैं, जो पास किए गए परमेश्‍वरों के आधार पर एक उपयुक्त आकृति लौटाएगा। जाहिर है, कारखाने को पता चल जाएगा कि किस प्रकार की आकृति बनाई जा रही है, लेकिन कॉलर के पास नहीं होगा इसके बारे में परवाह करने के लिए कि यह एक चक्र है, या एक वर्ग, या इसी तरह।

अब, कल्पना कीजिए कि आपके पास विभिन्न प्रकार के ऑपरेशन हैं जिन्हें आपको अपने आकार पर करना है। हो सकता है कि आपको उन्हें क्षेत्र के अनुसार क्रमबद्ध करने की आवश्यकता हो, उन सभी को एक नए आकार में सेट करें, और फिर उन्हें UI में प्रदर्शित करें। आकार सभी कारखाने द्वारा बनाए गए हैं और फिर सॉर्टर, सिज़र और डिस्प्ले कक्षाओं में बहुत आसानी से पारित किए जा सकते हैं। यदि आपको भविष्य में कुछ समय के लिए षट्भुज वर्ग जोड़ने की आवश्यकता है, तो आपको कारखाने के अलावा कुछ भी बदलने की आवश्यकता नहीं है। इंटरफ़ेस के बिना, एक और आकार जोड़ना एक बहुत ही गड़बड़ प्रक्रिया बन जाता है।


6

तुम यह कर सकते थे

Ibox myBox = new Rectangle();

इस तरह से आप इस ऑब्जेक्ट को Ibox के रूप में उपयोग कर रहे हैं और आपको इसकी वास्तव में परवाह नहीं है Rectangle


इसका मतलब है कि हम इस तरह लिख सकते हैं ?! > आयत उदाहरण = नया आयत ();
डॉ.जैक्य

@ Mr.Hyde आप बाद में जोड़ना चाहते हैं Squareआप एक समस्या होगा .... अगर आप इंटरफेस के बिना यह करने के लिए प्रयास करते हैं, आप गारंटी नहीं दे सकते कि Squareऔर Rectangleहै एक ही तरीके का ... इस जब आप एक बुरा सपना हो सकता है एक बड़ा कोड आधार ... याद रखें, इंटरफेस एक टेम्पलेट को परिभाषित करते हैं।
कोलोब कैनियन

6

क्यों आंतरिक ??????

इसकी शुरुआत कुत्ते से होती है। विशेष रूप से, एक पग

पग में विभिन्न व्यवहार हैं:

public class Pug { 
private String name;
public Pug(String n) { name = n; } 
public String getName() { return name; }  
public String bark() { return  "Arf!"; } 
public boolean hasCurlyTail() { return true; } }

और आपके पास एक लैब्राडोर है, जिसके पास व्यवहारों का एक सेट भी है।

public class Lab { 
private String name; 
public Lab(String n) { name = n; } 
public String getName() { return name; } 
public String bark() { return "Woof!"; } 
public boolean hasCurlyTail() { return false; } }

हम कुछ पग्स और लैब बना सकते हैं:

Pug pug = new Pug("Spot"); 
Lab lab = new Lab("Fido");

और हम उनके व्यवहार का आह्वान कर सकते हैं:

pug.bark() -> "Arf!" 
lab.bark() -> "Woof!" 
pug.hasCurlyTail() -> true 
lab.hasCurlyTail() -> false 
pug.getName() -> "Spot"

मान लीजिए कि मैं एक कुत्ते केनेल को चलाता हूं और मुझे उन सभी कुत्तों पर नज़र रखने की ज़रूरत है जो मैं आवास कर रहा हूं। मुझे अपने पग और लैब्राडोर को अलग-अलग सरणियों में संग्रहीत करने की आवश्यकता है :

public class Kennel { 
Pug[] pugs = new Pug[10]; 
Lab[] labs = new Lab[10];  
public void addPug(Pug p) { ... } 
public void addLab(Lab l) { ... } 
public void printDogs() { // Display names of all the dogs } }

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

इनसाइट: पग और लैब्राडोर (और पूडल) दोनों कुत्ते के प्रकार हैं और उनके व्यवहार का एक ही सेट है। यही है, हम कह सकते हैं (इस उदाहरण के प्रयोजनों के लिए) कि सभी कुत्ते भौंक सकते हैं, एक नाम है, और एक घुंघराले पूंछ हो सकती है या नहीं। हम सभी कुत्तों को क्या कर सकते हैं यह परिभाषित करने के लिए एक इंटरफ़ेस का उपयोग कर सकते हैं, लेकिन उन विशेष प्रकार के व्यवहारों को लागू करने के लिए इसे विशेष प्रकार के कुत्तों तक छोड़ सकते हैं। इंटरफ़ेस कहता है "यहां वे चीजें हैं जो सभी कुत्ते कर सकते हैं" लेकिन यह नहीं कहता कि प्रत्येक व्यवहार कैसे किया जाता है।

public interface Dog 
{
public String bark(); 
public String getName(); 
public boolean hasCurlyTail(); }

फिर मैंने डॉग व्यवहार को लागू करने के लिए पग और लैब कक्षाओं को थोड़ा बदल दिया। हम कह सकते हैं कि एक पग एक कुत्ता है और एक लैब एक कुत्ता है।

public class Pug implements Dog {
// the rest is the same as before } 

public class Lab implements Dog { 
// the rest is the same as before 
}

मैं अभी भी पग्स और लैब्स को इंस्टेंट कर सकता हूं जैसा कि मैंने पहले किया था, लेकिन अब मुझे इसे करने का एक नया तरीका भी मिल गया है:

Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido");

यह कहता है कि d1 केवल एक कुत्ता नहीं है, यह विशेष रूप से एक पग है। और d2 एक डॉग भी है, विशेष रूप से एक लैब। हम व्यवहार को लागू कर सकते हैं और वे पहले की तरह काम करते हैं:

d1.bark() -> "Arf!" 
d2.bark() -> "Woof!" 
d1.hasCurlyTail() -> true 
d2.hasCurlyTail() -> false 
d1.getName() -> "Spot"

यहाँ है जहाँ सभी अतिरिक्त काम बंद का भुगतान करता है। केनेल वर्ग बहुत सरल हो जाता है। मुझे केवल एक सरणी और एक ऐडडॉग विधि की आवश्यकता है। दोनों किसी भी वस्तु के साथ काम करेंगे जो एक कुत्ता है; वह है, डॉग इंटरफ़ेस को लागू करने वाली वस्तुएँ।

public class Kennel {
Dog[] dogs = new Dog[20]; 
public void addDog(Dog d) { ... } 
public void printDogs() {
// Display names of all the dogs } }

इसका उपयोग कैसे करें:

Kennel k = new Kennel(); 
Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido"); 
k.addDog(d1); 
k.addDog(d2); 
k.printDogs();

अंतिम विवरण प्रदर्शित होगा: स्पॉट फ़िदो

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


@niranjan kurambhatti मैं कुत्ते का विस्तार करने के लिए सभी कक्षाएं बना सकता हूं लेकिन फिर भी इंटरफ़ेस क्यों ??
जीव

3

संग्रह ढांचे में इंटरफेस का उपयोग कैसे किया जाता है, इसका एक बड़ा उदाहरण है। यदि आप एक फ़ंक्शन लिखते हैं जो एक लेता है List, तो यह कोई फर्क नहीं पड़ता कि उपयोगकर्ता एक Vectorया एक ArrayListया एक HashListया एक से गुजरता है । और आप इसे Listकिसी भी फ़ंक्शन Collectionया Iterableइंटरफ़ेस के लिए भी पास कर सकते हैं।

इसे लागू Collections.sort(List list)किए जाने की परवाह किए बिना, संभव जैसे कार्य करता Listहै।


3

यही कारण है कि फैक्ट्री पैटर्न और अन्य रचनात्मक पैटर्न जावा में बहुत लोकप्रिय हैं। आप सही हैं कि उनके बिना जावा तात्कालिकता के आसान अमूर्तता के लिए आउट ऑफ द बॉक्स तंत्र प्रदान नहीं करता है। फिर भी, आपको हर जगह अमूर्तता मिलती है जहाँ आप अपनी पद्धति में एक वस्तु नहीं बनाते हैं, जो आपके अधिकांश कोड में होनी चाहिए।

एक तरफ के रूप में, मैं आम तौर पर लोगों को नामकरण इंटरफेस के लिए "IRealname" तंत्र का पालन नहीं करने के लिए प्रोत्साहित करता हूं। यह एक विंडोज / COM चीज है जो हंगेरियन नोटेशन की कब्र में एक पैर रखता है और वास्तव में आवश्यक नहीं है (जावा पहले से ही दृढ़ता से टाइप किया गया है, और इंटरफेस होने का पूरा बिंदु उन्हें वर्ग के प्रकारों से यथासंभव अप्रभेद्य है)।


1
आप स्थिर टाइपिंग के साथ मजबूत टाइपिंग को भ्रमित कर रहे हैं।
एलजेन्सो

3

यह मत भूलो कि बाद की तारीख में आप एक मौजूदा वर्ग ले सकते हैं , और इसे लागू कर सकते हैं , और IBoxफिर यह आपके सभी बॉक्स-जागरूक कोड के लिए उपलब्ध हो जाएगा।

इंटरफेस नाम हैं, तो इसमें कुछ समय स्पष्ट हो जाता है सुलभ । जैसे

public interface Saveable {
....

public interface Printable {
....

आदि (नामकरण की योजनाएँ हमेशा काम नहीं करती हैं जैसे मुझे यकीन नहीं है कि Boxableयहाँ उपयुक्त है)


3

इंटरफेस का एकमात्र उद्देश्य यह सुनिश्चित करना है कि एक इंटरफ़ेस को जिस वर्ग ने लागू किया है, उसे सही तरीके मिले हैं जैसा कि एक इंटरफ़ेस द्वारा वर्णित है? या इंटरफेस का कोई अन्य उपयोग है?

मैं इंटरफ़ेस की नई विशेषताओं के साथ उत्तर को अपडेट कर रहा हूं, जिसे जावा 8 संस्करण के साथ पेश किया गया है ।

इंटरफ़ेस के सारांश पर oracle प्रलेखन पृष्ठ से :

एक इंटरफ़ेस घोषणा में शामिल हो सकता है

  1. विधि हस्ताक्षर
  2. डिफ़ॉल्ट विधियाँ
  3. स्थैतिक तरीके
  4. निरंतर परिभाषाएँ।

केवल वे विधियाँ हैं जिनमें क्रियान्वयन डिफ़ॉल्ट और स्थिर विधियाँ हैं।

इंटरफ़ेस के उपयोग :

  1. एक अनुबंध को परिभाषित करने के लिए
  2. असंबंधित वर्गों को जोड़ने के लिए एक क्षमताएं हैं (जैसे कि Serializableइंटरफ़ेस लागू करने वाली कक्षाएं उस इंटरफ़ेस को लागू करने के अलावा उनके बीच कोई संबंध नहीं हो सकती हैं या नहीं
  3. विनिमेय कार्यान्वयन प्रदान करने के लिए उदाहरण के लिए रणनीति पैटर्न
  4. डिफ़ॉल्ट विधियाँ आपको अपने पुस्तकालयों के इंटरफेस में नई कार्यक्षमता जोड़ने और उन इंटरफेस के पुराने संस्करणों के लिए लिखे गए कोड के साथ द्विआधारी संगतता सुनिश्चित करने में सक्षम बनाती हैं।
  5. अपने पुस्तकालयों में सहायक तरीकों के साथ सहायक विधियों को व्यवस्थित करें (आप एक अलग इंटरफ़ेस के बजाय एक ही इंटरफ़ेस में एक इंटरफ़ेस के लिए स्थैतिक तरीकों को विशिष्ट रख सकते हैं)

सार वर्ग और इंटरफ़ेस के बीच अंतर और काम के उदाहरणों के साथ मामलों के उपयोग के संबंध में कुछ एसई प्रश्न :

इंटरफ़ेस और अमूर्त वर्ग के बीच अंतर क्या है?

मुझे इंटरफ़ेस और अमूर्त वर्ग के बीच अंतर कैसे समझाया जाना चाहिए?

जावा 8 में जोड़े गए नए फीचर्स को समझने के लिए डॉक्यूमेंटेशन पेज पर एक नजर डालें : डिफॉल्ट तरीके और स्टैटिक मेथड


मैं जावा-8 टैग निकाल दिया गया के रूप में प्रश्न (जावा -8 से पहले और वास्तव में पूछा गया था लंबे समय) जावा-8 के बारे में कुछ भी नहीं कहा था। टैग सवालों के लिए हैं, जवाब के लिए नहीं।
टैगिर वलेव

2

इंटरफेस का उद्देश्य कार्यान्वयन से अमूर्त या विघटित करना है।

यदि आप अपने कार्यक्रम में एक अमूर्तता का परिचय देते हैं, तो आप संभावित कार्यान्वयन की परवाह नहीं करते हैं। आप यह क्या कर सकते हैं और कैसे नहीं में रुचि रखते हैं , और आप interfaceजावा में इसे व्यक्त करने के लिए उपयोग करते हैं ।


सभी संरचित प्रोग्रामिंग का उद्देश्य अमूर्तता है। आप यह क्यों कहेंगे कि इंटरफेस का उद्देश्य अमूर्त है, क्योंकि मैं जेनेरिक और वर्ग रचना का उपयोग करके सटीक एक ही चीज़ प्राप्त कर सकता हूं?
Apocalisp

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

1

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


1

ऐसे स्थान जहां एक भ्रूण को कई वंशानुक्रम की अनुमति देने के लिए जावा में जोड़ा जाता है। जावा के डेवलपर्स ने हालांकि यह महसूस किया कि एक से अधिक विरासत होना एक "खतरनाक" विशेषता थी, यही कारण है कि एक इंटरफ़ेस का विचार आया।

एकाधिक वंशानुक्रम खतरनाक है क्योंकि आपके पास निम्न की तरह एक वर्ग हो सकता है:


class Box{
    public int getSize(){
       return 0;
    }
    public int getArea(){
       return 1;
    }

}

class Triangle{
    public int getSize(){
       return 1;
    }
    public int getArea(){
       return 0;
    }

}

class FunckyFigure extends Box, Triable{
   // we do not implement the methods we will used the inherited ones
}

यह वह विधि होगी जिसका उपयोग करते समय हमें बुलाया जाना चाहिए


   FunckyFigure.GetArea(); 

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


आप अपने उत्तर में कई कार्यान्वयन वंशानुक्रम और एकाधिक इंटरफ़ेस वंशानुक्रम के बीच अंतर करना चाहते हैं, अन्यथा यह भ्रमित हो जाता है।
एलजेन्सो

0

यहाँ इंटरफ़ेस लाभ की मेरी समझ है। अगर मैं ग़लत हूं तो मेरी गलती सुझाएं। कल्पना कीजिए कि हम OS विकसित कर रहे हैं और अन्य टीम कुछ उपकरणों के लिए ड्राइवरों को विकसित कर रही है। तो हमने एक Storage StorageDevice विकसित किया है। हमारे पास इसके दो कार्यान्वयन हैं (FDD और HDD) जो अन्य डेवलपर्स टीम द्वारा प्रदान किए गए हैं।

तब हमारे पास एक OperatingSystem क्लास है, जो StorageDevice इंटरफ़ेस को लागू किए गए वर्ग के एक उदाहरण को पारित करके saveData जैसी इंटरफ़ेस विधियों को कॉल कर सकता है।

यहां लाभ यह है कि हम इंटरफ़ेस के कार्यान्वयन के बारे में परवाह नहीं करते हैं। अन्य टीम StorageDevice इंटरफ़ेस को लागू करके काम करेगी।

package mypack;

interface StorageDevice {
    void saveData (String data);
}


class FDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to floppy drive! Data: "+data);
    }
}

class HDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to hard disk drive! Data: "+data);
    }
}

class OperatingSystem {
    public String name;
    StorageDevice[] devices;
    public OperatingSystem(String name, StorageDevice[] devices) {

        this.name = name;
        this.devices = devices.clone();

        System.out.println("Running OS " + this.name);
        System.out.println("List with storage devices available:");
        for (StorageDevice s: devices) {
            System.out.println(s);
        }

    }

    public void saveSomeDataToStorageDevice (StorageDevice storage, String data) {
        storage.saveData(data);
    }
}

public class Main {

    public static void main(String[] args) {

        StorageDevice fdd0 = new FDD();
        StorageDevice hdd0 = new HDD();     
        StorageDevice[] devs = {fdd0, hdd0};        
        OperatingSystem os = new OperatingSystem("Linux", devs);
        os.saveSomeDataToStorageDevice(fdd0, "blah, blah, blah...");    
    }
}

एक ही चीज अमूर्त वर्ग StorageDevice और FDD और HDD वर्गों के साथ StorageDevice वर्ग का विस्तार किया जा सकता है। लेकिन अगर हम सार वर्ग का उपयोग करते हैं तो हम कई उत्तराधिकार का लाभ नहीं ले सकते।
व्लादिमीर जॉर्जीव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.