Java Language
विरासत
खोज…
परिचय
वंशानुक्रम एक मूल वस्तु उन्मुख विशेषता है जिसमें एक वर्ग एक दूसरे वर्ग के गुणों को प्राप्त करता है और विस्तारित करता है, कीवर्ड का उपयोग extends
। इंटरफ़ेस और कीवर्ड implements
, इंटरफ़ेस देखें।
वाक्य - विन्यास
- कक्षा ClassB का विस्तार ClassA {...}
- क्लास क्लास बी इंटरफ़ेस लागू करता है {...}
- इंटरफ़ेस इंटरफ़ेसबी इंटरफ़ेस इंटरफ़ेस बढ़ाता है {...}
- क्लास क्लासबी ने क्लासए को लागू किया इंटरफ़ेस इंटरफ़ेस, इंटरफेसडी {...}
- सार वर्ग AbstractClassB ClassA {...} का विस्तार करता है
- अमूर्त वर्ग AbstractClassB का विस्तार AbstractClassA {...}
- अमूर्त वर्ग
टिप्पणियों
वंशानुक्रम को अक्सर जेनरिक के साथ जोड़ दिया जाता है ताकि आधार वर्ग में एक या अधिक प्रकार के पैरामीटर हों। एक सामान्य वर्ग बनाना देखें।
सार वर्ग
एक अमूर्त वर्ग एक वर्ग है जो abstract
खोजशब्द के साथ चिह्नित है। यह, गैर-अमूर्त वर्ग के विपरीत, इसमें अमूर्त - कार्यान्वयन-कम - विधियाँ हो सकती हैं। हालांकि, यह अमूर्त तरीकों के बिना एक अमूर्त वर्ग बनाने के लिए मान्य है।
एक सार वर्ग को तत्काल नहीं किया जा सकता है। यह उप-वर्गीय (विस्तारित) हो सकता है जब तक कि उप-वर्ग या तो सार है, या सुपर क्लास द्वारा सार के रूप में चिह्नित सभी विधियों को लागू करता है।
एक सार वर्ग का एक उदाहरण:
public abstract class Component {
private int x, y;
public setPosition(int x, int y) {
this.x = x;
this.y = y;
}
public abstract void render();
}
कक्षा को सार के रूप में चिह्नित किया जाना चाहिए, जब उसमें कम से कम एक सार विधि हो। अमूर्त विधि एक ऐसी विधि है जिसका कोई कार्यान्वयन नहीं है। अन्य तरीकों को एक अमूर्त वर्ग के भीतर घोषित किया जा सकता है जो किसी भी उप-वर्गों के लिए सामान्य कोड प्रदान करने के लिए कार्यान्वयन है।
इस वर्ग को तत्काल करने का प्रयास एक संकलित त्रुटि प्रदान करेगा:
//error: Component is abstract; cannot be instantiated
Component myComponent = new Component();
हालांकि एक वर्ग जो Component
विस्तार करता है, और अपने सभी अमूर्त तरीकों के लिए एक कार्यान्वयन प्रदान करता है और इसे तत्काल किया जा सकता है।
public class Button extends Component {
@Override
public void render() {
//render a button
}
}
public class TextBox extends Component {
@Override
public void render() {
//render a textbox
}
}
वंशानुक्रम वर्गों के उदाहरणों को मूल वर्ग (सामान्य वंशानुक्रम) के रूप में भी डाला जा सकता है और वे अमूर्त पद्धति के होने पर बहुरूप प्रभाव प्रदान करते हैं।
Component myButton = new Button();
Component myTextBox = new TextBox();
myButton.render(); //renders a button
myTextBox.render(); //renders a text box
सार कक्षाएं बनाम इंटरफेस
अमूर्त वर्ग और इंटरफेस दोनों कार्यान्वयन प्रदान करने के लिए विस्तार / कार्यान्वयन वर्ग की आवश्यकता करते हुए विधि हस्ताक्षर को परिभाषित करने का एक तरीका प्रदान करते हैं।
अमूर्त वर्गों और इंटरफेस के बीच दो प्रमुख अंतर हैं:
- एक वर्ग केवल एक ही वर्ग का विस्तार कर सकता है, लेकिन कई इंटरफेस लागू कर सकता है।
- एक अमूर्त वर्ग में उदाहरण (गैर-
static
) फ़ील्ड हो सकते हैं, लेकिन इंटरफ़ेस में केवलstatic
फ़ील्ड हो सकते हैं।
इंटरफेस में घोषित तरीकों में कार्यान्वयन शामिल नहीं हो सकते हैं, इसलिए अमूर्त वर्गों का उपयोग तब किया जाता था जब यह अतिरिक्त तरीकों को प्रदान करने के लिए उपयोगी होता था जो कार्यान्वयन को अमूर्त विधियों कहा जाता है।
जावा 8 इंटरफेस को डिफ़ॉल्ट तरीकों को शामिल करने की अनुमति देता है, आमतौर पर इंटरफ़ेस के अन्य तरीकों का उपयोग करके लागू किया जाता है , इस संबंध में इंटरफेस और अमूर्त कक्षाएं समान रूप से शक्तिशाली बनाते हैं।
सार वर्ग के अनाम उपवर्ग
एक सुविधा के रूप में जावा, अमूर्त वर्गों के उपवर्गों के अनाम उदाहरणों की तात्कालिकता की अनुमति देता है, जो नई वस्तु बनाने पर अमूर्त विधियों के लिए कार्यान्वयन प्रदान करते हैं। उपरोक्त उदाहरण का उपयोग करके यह इस तरह दिख सकता है:
Component myAnonymousComponent = new Component() {
@Override
public void render() {
// render a quick 1-time use component
}
}
स्टेटिक इनहेरिटेंस
स्थैतिक विधि को सामान्य तरीकों के समान विरासत में मिला जा सकता है, हालांकि सामान्य तरीकों के विपरीत स्थिर विधि को ओवरराइड करने के लिए मजबूर करने के लिए " अमूर्त " तरीके बनाना असंभव है। सुपर क्लास में स्टैटिक मेथड के रूप में एक ही सिग्नेचर वाला तरीका लिखना ओवरराइडिंग का एक रूप प्रतीत होता है, लेकिन वास्तव में यह एक नया फ़ंक्शन बनाता है जो दूसरे को छुपाता है।
public class BaseClass {
public static int num = 5;
public static void sayHello() {
System.out.println("Hello");
}
public static void main(String[] args) {
BaseClass.sayHello();
System.out.println("BaseClass's num: " + BaseClass.num);
SubClass.sayHello();
//This will be different than the above statement's output, since it runs
//A different method
SubClass.sayHello(true);
StaticOverride.sayHello();
System.out.println("StaticOverride's num: " + StaticOverride.num);
}
}
public class SubClass extends BaseClass {
//Inherits the sayHello function, but does not override it
public static void sayHello(boolean test) {
System.out.println("Hey");
}
}
public static class StaticOverride extends BaseClass {
//Hides the num field from BaseClass
//You can even change the type, since this doesn't affect the signature
public static String num = "test";
//Cannot use @Override annotation, since this is static
//This overrides the sayHello method from BaseClass
public static void sayHello() {
System.out.println("Static says Hi");
}
}
इनमें से किसी भी वर्ग को चलाने से आउटपुट उत्पन्न होता है:
Hello
BaseClass's num: 5
Hello
Hey
Static says Hi
StaticOverride's num: test
ध्यान दें कि सामान्य वंशानुक्रम के विपरीत, स्थिर वंशानुक्रम विधियों में छिपा नहीं है। आप हमेशा sayHello
मेथड को BaseClass.sayHello()
का उपयोग करके कॉल कर सकते हैं। लेकिन कक्षाएं स्टैटिक विधियों को इनहेरिट करती हैं यदि उप हस्ताक्षर में समान हस्ताक्षर वाले कोई तरीके नहीं मिलते हैं। यदि दो विधि के हस्ताक्षर भिन्न होते हैं, तो दोनों विधियाँ उपवर्ग से चलाई जा सकती हैं, भले ही नाम एक ही हो।
स्थैतिक क्षेत्र एक दूसरे को इसी तरह से छिपाते हैं।
विरासत और ओवरराइडिंग को प्रतिबंधित करने के लिए 'अंतिम' का उपयोग करना
अंतिम कक्षाएं
जब एक class
घोषणा में उपयोग किया जाता है, तो final
संशोधक अन्य वर्गों को घोषित होने से रोकता है जो कक्षा का extend
करते हैं। एक final
वर्ग वंशानुक्रम वर्ग पदानुक्रम में एक "पत्ती" वर्ग है।
// This declares a final class
final class MyFinalClass {
/* some code */
}
// Compilation error: cannot inherit from final MyFinalClass
class MySubClass extends MyFinalClass {
/* more code */
}
अंतिम वर्गों के लिए उपयोग-मामले
किसी वर्ग की तात्कालिकता को नियंत्रित करने या रोकने के लिए अंतिम कक्षाओं को एक private
निर्माणकर्ता के साथ जोड़ा जा सकता है। इसका उपयोग तथाकथित "उपयोगिता वर्ग" बनाने के लिए किया जा सकता है जो केवल स्थैतिक सदस्यों को परिभाषित करता है; अर्थात् स्थिरांक और स्थिर विधियाँ।
public final class UtilityClass {
// Private constructor to replace the default visible constructor
private UtilityClass() {}
// Static members can still be used as usual
public static int doSomethingCool() {
return 123;
}
}
अपरिवर्तनीय वर्गों को भी final
घोषित किया जाना चाहिए। (एक अपरिवर्तनीय वर्ग वह है जिसके उदाहरणों को उनके बनाए जाने के बाद नहीं बदला जा सकता है; I mmutable ऑब्जेक्ट्स विषय देखें।) ऐसा करने से, आप एक अपरिवर्तनीय वर्ग के एक उत्परिवर्ती उपवर्ग को बनाना असंभव बनाते हैं। यह लिस्कोव सबस्टीट्यूशन सिद्धांत का उल्लंघन करेगा जिसके लिए आवश्यक है कि एक उपप्रकार को अपने सुपरसीप के "व्यवहार अनुबंध" का पालन करना चाहिए।
व्यावहारिक दृष्टिकोण से, एक अपरिवर्तनीय वर्ग को final
घोषित करने से कार्यक्रम व्यवहार के बारे में तर्क करना आसान हो जाता है। यह उस परिदृश्य में सुरक्षा चिंताओं को भी संबोधित करता है जहां एक सुरक्षा सैंडबॉक्स में अविश्वसनीय कोड निष्पादित किया जाता है। (उदाहरण के लिए, चूंकि String
को final
रूप में घोषित किया गया है, इसलिए एक विश्वसनीय वर्ग को यह चिंता करने की आवश्यकता नहीं है कि यह परिवर्तनशील उपवर्ग को स्वीकार करने में धोखा दिया जा सकता है, जिसे अविश्वसनीय कॉल करने वाला फिर बदल सकता है।)
final
वर्गों का एक नुकसान यह है कि वे मॉकिटो जैसे कुछ नकली ढांचे के साथ काम नहीं करते हैं। अपडेट: मॉकिटो संस्करण 2 अब अंतिम कक्षाओं का समर्थन करता है।
अंतिम तरीके
उप-वर्गों में उन्हें ओवरराइड होने से बचाने के लिए final
संशोधक विधियों पर भी लागू किया जा सकता है:
public class MyClassWithFinalMethod {
public final void someMethod() {
}
}
public class MySubClass extends MyClassWithFinalMethod {
@Override
public void someMethod() { // Compiler error (overridden method is final)
}
}
अंतिम तरीके आमतौर पर तब उपयोग किए जाते हैं जब आप यह प्रतिबंधित करना चाहते हैं कि उपवर्गों को पूरी तरह से मना किए बिना एक उपवर्ग क्या बदल सकता है।
final
संशोधक को चर पर भी लागू किया जा सकता है, लेकिन चर के लिए final
का अर्थ विरासत से असंबंधित है।
Liskov प्रतिस्थापन सिद्धांत
सबस्टीलिबिलिटी 1987 के सम्मेलन के मुख्य वक्ता के रूप में बारबरा लिस्कोव द्वारा शुरू की गई ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग में एक सिद्धांत है, जिसमें कहा गया है कि यदि क्लास B
क्लास A
का उपवर्ग है, तो जहां भी A
की उम्मीद है, इसके बजाय B
का उपयोग किया जा सकता है:
class A {...}
class B extends A {...}
public void method(A obj) {...}
A a = new B(); // Assignment OK
method(new B()); // Passing as parameter OK
यह तब भी लागू होता है जब प्रकार एक इंटरफ़ेस होता है, जहाँ वस्तुओं के बीच किसी भी पदानुक्रमित संबंध की आवश्यकता नहीं होती है:
interface Foo {
void bar();
}
class A implements Foo {
void bar() {...}
}
class B implements Foo {
void bar() {...}
}
List<Foo> foos = new ArrayList<>();
foos.add(new A()); // OK
foos.add(new B()); // OK
अब सूची में ऐसी वस्तुएँ हैं जो एक ही वर्ग पदानुक्रम से नहीं हैं।
विरासत
कक्षाओं में extends
कीवर्ड के उपयोग के साथ, सुपरक्लास की सभी संपत्तियां (जो कि पैरेंट क्लास या बेस क्लास के रूप में भी जानी जाती हैं) सबक्लास में मौजूद हैं ( चाइल्ड क्लास या व्युत्पन्न वर्ग के रूप में भी जाना जाता है)
public class BaseClass {
public void baseMethod(){
System.out.println("Doing base class stuff");
}
}
public class SubClass extends BaseClass {
}
SubClass
उदाहरणों को विधि baseMethod()
विरासत में मिला है baseMethod()
:
SubClass s = new SubClass();
s.baseMethod(); //Valid, prints "Doing base class stuff"
अतिरिक्त सामग्री को एक उपवर्ग में जोड़ा जा सकता है। ऐसा करना आधार वर्ग में किसी भी बदलाव के बिना उपवर्ग में अतिरिक्त कार्यक्षमता के लिए अनुमति देता है या उसी आधार श्रेणी से किसी भी अन्य उपवर्ग का उपयोग करता है:
public class Subclass2 extends BaseClass {
public void anotherMethod() {
System.out.println("Doing subclass2 stuff");
}
}
Subclass2 s2 = new Subclass2();
s2.baseMethod(); //Still valid , prints "Doing base class stuff"
s2.anotherMethod(); //Also valid, prints "Doing subclass2 stuff"
क्षेत्र भी विरासत में मिले हैं:
public class BaseClassWithField {
public int x;
}
public class SubClassWithField extends BaseClassWithField {
public SubClassWithField(int x) {
this.x = x; //Can access fields
}
}
private
क्षेत्र और विधियाँ अभी भी उपवर्ग के भीतर मौजूद हैं, लेकिन वे सुलभ नहीं हैं:
public class BaseClassWithPrivateField {
private int x = 5;
public int getX() {
return x;
}
}
public class SubClassInheritsPrivateField extends BaseClassWithPrivateField {
public void printX() {
System.out.println(x); //Illegal, can't access private field x
System.out.println(getX()); //Legal, prints 5
}
}
SubClassInheritsPrivateField s = new SubClassInheritsPrivateField();
int x = s.getX(); //x will have a value of 5.
जावा में, प्रत्येक वर्ग अधिकतम एक अन्य वर्ग में विस्तार कर सकता है।
public class A{}
public class B{}
public class ExtendsTwoClasses extends A, B {} //Illegal
इसे मल्टीपल इनहेरिटेंस के रूप में जाना जाता है, और कुछ भाषाओं में कानूनी होने के बावजूद, जावा इसे कक्षाओं के साथ अनुमति नहीं देता है।
इसके परिणामस्वरूप, हर कक्षा में Object
लिए जाने वाली कक्षाओं की एक असहनीय पैतृक श्रृंखला होती है, जिसमें से सभी वर्ग उतरते हैं।
वंशानुक्रम और स्थैतिक तरीके
जावा में, माता-पिता और बच्चे की कक्षा दोनों में एक ही नाम के साथ स्थिर तरीके हो सकते हैं। लेकिन ऐसे मामलों में बच्चे में स्थैतिक विधि का कार्यान्वयन अभिभावक वर्ग के कार्यान्वयन को छिपा रहा है , यह विधि अधिभावी नहीं है। उदाहरण के लिए:
class StaticMethodTest {
// static method and inheritance
public static void main(String[] args) {
Parent p = new Child();
p.staticMethod(); // prints Inside Parent
((Child) p).staticMethod(); // prints Inside Child
}
static class Parent {
public static void staticMethod() {
System.out.println("Inside Parent");
}
}
static class Child extends Parent {
public static void staticMethod() {
System.out.println("Inside Child");
}
}
}
स्टेटिक तरीके एक उदाहरण के लिए एक वर्ग से बंधे होते हैं और यह विधि बाध्यकारी समय पर होती है। चूंकि staticMethod()
लिए पहली कॉल में, पैरेंट क्लास रेफरेंस p
का उपयोग किया गया था, staticMethod()
का Parent
संस्करण है। दूसरे मामले में, हम कलाकारों किया p
में Child
वर्ग, Child
के staticMethod()
निष्पादित।
परिवर्तनशील छायांकन
वेरिएबल्स को शोल्ड किया जाता है और विधियाँ ओवरराइड की जाती हैं। किस चर का उपयोग किया जाएगा यह उस वर्ग पर निर्भर करता है जिसे चर घोषित किया गया है। किस विधि का उपयोग किया जाएगा यह चर द्वारा संदर्भित ऑब्जेक्ट के वास्तविक वर्ग पर निर्भर करता है।
class Car {
public int gearRatio = 8;
public String accelerate() {
return "Accelerate : Car";
}
}
class SportsCar extends Car {
public int gearRatio = 9;
public String accelerate() {
return "Accelerate : SportsCar";
}
public void test() {
}
public static void main(String[] args) {
Car car = new SportsCar();
System.out.println(car.gearRatio + " " + car.accelerate());
// will print out 8 Accelerate : SportsCar
}
}
ऑब्जेक्ट संदर्भों का संकीर्ण और चौड़ा होना
बेस क्लास का एक उपवर्ग के रूप में उदाहरण देना: b = (B) a;
को संकीर्णता कहा जाता है (जैसा कि आप बेस क्लास ऑब्जेक्ट को और अधिक विशिष्ट क्लास ऑब्जेक्ट को संकीर्ण करने की कोशिश कर रहे हैं) और एक स्पष्ट प्रकार-कास्ट की आवश्यकता है।
एक बेस क्लास के लिए एक उपवर्ग का एक उदाहरण के रूप में कास्टिंग: A a = b;
को चौड़ा करना कहा जाता है और उसे टाइप-कास्ट की आवश्यकता नहीं होती है।
वर्णन करने के लिए, निम्नलिखित वर्ग घोषणाओं और परीक्षण कोड पर विचार करें:
class Vehicle {
}
class Car extends Vehicle {
}
class Truck extends Vehicle {
}
class MotorCycle extends Vehicle {
}
class Test {
public static void main(String[] args) {
Vehicle vehicle = new Car();
Car car = new Car();
vehicle = car; // is valid, no cast needed
Car c = vehicle // not valid
Car c = (Car) vehicle; //valid
}
}
बयान Vehicle vehicle = new Car();
एक वैध जावा स्टेटमेंट है। Car
का हर उदाहरण एक Vehicle
भी है। इसलिए, एक स्पष्ट प्रकार-डाली की आवश्यकता के बिना असाइनमेंट कानूनी है।
दूसरी ओर, Car c = vehicle;
मान्य नहीं है। vehicle
चर का स्थिर प्रकार Vehicle
जिसका अर्थ है कि यह Car
, ट्रक ,
मोटर साइकिल , or any other current or future subclass of
वाहन के , or any other current or future subclass of
उदाहरण दे सकता है . (Or indeed, an instance of
वाहन . (Or indeed, an instance of
itself, since we did not declare it as an
अमूर्त class.) The assignment cannot be allowed, since that might lead to
itself, since we did not declare it as an
class.) The assignment cannot be allowed, since that might lead to
ट्रक के उदाहरण का referring to a
।
इस स्थिति को रोकने के लिए, हमें एक स्पष्ट प्रकार-कास्ट जोड़ना होगा:
Car c = (Car) vehicle;
टाइप-कास्ट संकलक कि हम उम्मीद करते हैं का मूल्य बताता है vehicle
एक होने के लिए Car
या का एक उपवर्ग Car
। यदि आवश्यक हो, तो संकलक एक रन-टाइम प्रकार की जांच करने के लिए कोड सम्मिलित करेगा। यदि चेक विफल हो जाता है, तो कोड निष्पादित होने पर ClassCastException
को फेंक दिया जाएगा।
ध्यान दें कि सभी प्रकार के कास्ट मान्य नहीं हैं। उदाहरण के लिए:
String s = (String) vehicle; // not valid
जावा कंपाइलर जानता है कि एक उदाहरण जो Vehicle
के साथ संगत है, वह कभी String
के साथ संगत नहीं हो सकता है । टाइप-कास्ट कभी सफल नहीं हो सकता है, और जेएलएस यह कहता है कि यह संकलन त्रुटि देता है।
एक अंतरफलक के लिए प्रोग्रामिंग
एक इंटरफेस के लिए प्रोग्रामिंग के पीछे का विचार कोड को प्राथमिक रूप से इंटरफेस पर आधारित करना है और केवल तात्कालिकता के समय कंक्रीट कक्षाओं का उपयोग करना है। इस संदर्भ में, उदाहरण के लिए जावा कोड के साथ काम करने वाला अच्छा कोड कुछ इस तरह दिखेगा (ऐसा नहीं है कि यह विधि अपने आप में किसी काम की है, बस उदाहरण के लिए):
public <T> Set<T> toSet(Collection<T> collection) {
return Sets.newHashSet(collection);
}
जबकि बुरा कोड इस तरह दिख सकता है:
public <T> HashSet<T> toSet(ArrayList<T> collection) {
return Sets.newHashSet(collection);
}
न केवल पूर्व को तर्कों की एक व्यापक पसंद पर लागू किया जा सकता है, इसके परिणाम अन्य डेवलपर्स द्वारा प्रदान किए गए कोड के साथ अधिक संगत होंगे जो आमतौर पर एक इंटरफेस में प्रोग्रामिंग की अवधारणा का पालन करते हैं। हालांकि, पूर्व का उपयोग करने के लिए सबसे महत्वपूर्ण कारण हैं:
- अधिकांश समय संदर्भ, जिसमें परिणाम का उपयोग किया जाता है, को ठोस कार्यान्वयन के रूप में कई विवरणों की आवश्यकता नहीं होती है और न ही होनी चाहिए;
- इंटरफ़ेस कोड का पालन करना क्लीनर कोड और कम हैक जैसे कि एक और सार्वजनिक विधि कुछ विशिष्ट परिदृश्य की सेवा करने वाले वर्ग में जुड़ जाती है;
- कोड अधिक परीक्षण योग्य है क्योंकि इंटरफेस आसानी से नकली हैं;
- अंत में, अवधारणा तब भी मदद करती है जब केवल एक कार्यान्वयन अपेक्षित हो (कम से कम परीक्षण क्षमता के लिए)।
तो कोई एक विशेष कार्यान्वयन को ध्यान में रखते हुए नए कोड लिखते समय इंटरफ़ेस की प्रोग्रामिंग को आसानी से कैसे लागू कर सकता है? एक विकल्प जो हम आमतौर पर उपयोग करते हैं, वह निम्न पैटर्न का एक संयोजन है:
- एक इंटरफ़ेस के लिए प्रोग्रामिंग
- फ़ैक्टरी
- निर्माता
इन सिद्धांतों के आधार पर निम्न उदाहरण एक आरपीसी कार्यान्वयन का एक सरलीकृत और छंटनी संस्करण है, जो विभिन्न प्रोटोकॉलों के लिए लिखा गया है:
public interface RemoteInvoker {
<RQ, RS> CompletableFuture<RS> invoke(RQ request, Class<RS> responseClass);
}
उपरोक्त इंटरफ़ेस को फ़ैक्टरी के माध्यम से सीधे इंस्टैंट नहीं किया जा सकता है, इसके बजाय हम और अधिक ठोस इंटरफेस प्राप्त करते हैं, एक HTTP इन्वोकेशन के लिए और एक एएमक्यूपी के लिए, प्रत्येक में फिर एक फैक्ट्री और एक बिल्डर होने के लिए इंस्टेंस का निर्माण होता है, जो बदले में भी उदाहरण हैं उपरोक्त इंटरफ़ेस:
public interface AmqpInvoker extends RemoteInvoker {
static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
return new AmqpInvokerBuilder(instanceId, factory);
}
}
AMQP के साथ उपयोग के लिए RemoteInvoker
उदाहरणों को अब बिल्डर के आधार पर आसान (या अधिक शामिल) बनाया जा सकता है:
RemoteInvoker invoker = AmqpInvoker.with(instanceId, factory)
.requestRouter(router)
.build();
और एक अनुरोध का एक निमंत्रण के रूप में आसान है:
Response res = invoker.invoke(new Request(data), Response.class).get();
जावा 8 के स्थैतिक तरीकों को सीधे इंटरफेस में रखने की अनुमति के कारण, मध्यवर्ती फैक्ट्री उपरोक्त कोड में निहित हो गई है, जिसे AmqpInvoker.with()
साथ बदल दिया गया है। संस्करण 8 से पहले जावा में, एक ही प्रभाव एक आंतरिक Factory
वर्ग के साथ प्राप्त किया जा सकता है:
public interface AmqpInvoker extends RemoteInvoker {
class Factory {
public static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
return new AmqpInvokerBuilder(instanceId, factory);
}
}
}
तत्संबंधी तात्कालिकता तब बदल जाएगी:
RemoteInvoker invoker = AmqpInvoker.Factory.with(instanceId, factory)
.requestRouter(router)
.build();
ऊपर इस्तेमाल किया गया बिल्डर इस तरह दिख सकता है (हालांकि यह एक सरलीकरण है क्योंकि वास्तविक एक में अधिकतम 15 मापदंडों को परिभाषित करने की अनुमति है जो डिफॉल्ट से भटक रहा है)। ध्यान दें कि निर्माण सार्वजनिक नहीं है, इसलिए यह विशेष रूप से उपरोक्त AmqpInvoker
इंटरफ़ेस से केवल उपयोग करने योग्य है:
public class AmqpInvokerBuilder {
...
AmqpInvokerBuilder(String instanceId, ConnectionFactory factory) {
this.instanceId = instanceId;
this.factory = factory;
}
public AmqpInvokerBuilder requestRouter(RequestRouter requestRouter) {
this.requestRouter = requestRouter;
return this;
}
public AmqpInvoker build() throws TimeoutException, IOException {
return new AmqpInvokerImpl(instanceId, factory, requestRouter);
}
}
आमतौर पर, एक बिल्डर FreeBuilder जैसे उपकरण का उपयोग करके भी उत्पन्न किया जा सकता है।
अंत में, इस इंटरफेस के मानक (और केवल अपेक्षित) कार्यान्वयन को इंटरफ़ेस, कारखाने और बिल्डर के उपयोग को लागू करने के लिए एक पैकेज-स्थानीय वर्ग के रूप में परिभाषित किया गया है:
class AmqpInvokerImpl implements AmqpInvoker {
AmqpInvokerImpl(String instanceId, ConnectionFactory factory, RequestRouter requestRouter) {
...
}
@Override
public <RQ, RS> CompletableFuture<RS> invoke(final RQ request, final Class<RS> respClass) {
...
}
}
इस बीच, यह पैटर्न हमारे सभी नए कोड को विकसित करने में बहुत कुशल साबित हुआ, भले ही कार्यक्षमता कितनी भी सरल या जटिल क्यों न हो।
सार वर्ग और इंटरफ़ेस का उपयोग: "इस-ए" संबंध बनाम "हैस-ए" क्षमता है
अमूर्त कक्षाओं का उपयोग कब करें: कई संबंधित वस्तुओं के बीच एक ही या अलग व्यवहार को लागू करने के लिए
जब इंटरफेस का उपयोग करने के लिए: कई असंबंधित वस्तुओं द्वारा एक अनुबंध को लागू करने के लिए
एब्सट्रैक्ट क्लासेस "एक" संबंध है जबकि इंटरफेस प्रदान करते हैं "एक" क्षमता है।
इसे नीचे दिए गए कोड में देखा जा सकता है:
public class InterfaceAndAbstractClassDemo{
public static void main(String args[]){
Dog dog = new Dog("Jack",16);
Cat cat = new Cat("Joe",20);
System.out.println("Dog:"+dog);
System.out.println("Cat:"+cat);
dog.remember();
dog.protectOwner();
Learn dl = dog;
dl.learn();
cat.remember();
cat.protectOwner();
Climb c = cat;
c.climb();
Man man = new Man("Ravindra",40);
System.out.println(man);
Climb cm = man;
cm.climb();
Think t = man;
t.think();
Learn l = man;
l.learn();
Apply a = man;
a.apply();
}
}
abstract class Animal{
String name;
int lifeExpentency;
public Animal(String name,int lifeExpentency ){
this.name = name;
this.lifeExpentency=lifeExpentency;
}
public abstract void remember();
public abstract void protectOwner();
public String toString(){
return this.getClass().getSimpleName()+":"+name+":"+lifeExpentency;
}
}
class Dog extends Animal implements Learn{
public Dog(String name,int age){
super(name,age);
}
public void remember(){
System.out.println(this.getClass().getSimpleName()+" can remember for 5 minutes");
}
public void protectOwner(){
System.out.println(this.getClass().getSimpleName()+ " will protect owner");
}
public void learn(){
System.out.println(this.getClass().getSimpleName()+ " can learn:");
}
}
class Cat extends Animal implements Climb {
public Cat(String name,int age){
super(name,age);
}
public void remember(){
System.out.println(this.getClass().getSimpleName() + " can remember for 16 hours");
}
public void protectOwner(){
System.out.println(this.getClass().getSimpleName()+ " won't protect owner");
}
public void climb(){
System.out.println(this.getClass().getSimpleName()+ " can climb");
}
}
interface Climb{
void climb();
}
interface Think {
void think();
}
interface Learn {
void learn();
}
interface Apply{
void apply();
}
class Man implements Think,Learn,Apply,Climb{
String name;
int age;
public Man(String name,int age){
this.name = name;
this.age = age;
}
public void think(){
System.out.println("I can think:"+this.getClass().getSimpleName());
}
public void learn(){
System.out.println("I can learn:"+this.getClass().getSimpleName());
}
public void apply(){
System.out.println("I can apply:"+this.getClass().getSimpleName());
}
public void climb(){
System.out.println("I can climb:"+this.getClass().getSimpleName());
}
public String toString(){
return "Man :"+name+":Age:"+age;
}
}
उत्पादन:
Dog:Dog:Jack:16
Cat:Cat:Joe:20
Dog can remember for 5 minutes
Dog will protect owner
Dog can learn:
Cat can remember for 16 hours
Cat won't protect owner
Cat can climb
Man :Ravindra:Age:40
I can climb:Man
I can think:Man
I can learn:Man
I can apply:Man
मुख्य नोट:
साझा विशेषताओं के साथ
Animal
एक सार वर्ग है:name
औरlifeExpectancy
और अमूर्त विधियाँ:remember()
औरprotectOwner()
।Dog
औरCat
ऐसेAnimals
हैं जिन्होंनेremember()
औरprotectOwner()
विधियों को लागू कियाremember()
।Cat
climb()
सकतीclimb()
लेकिनDog
नहीं कर सकता।Dog
think()
सकताthink()
लेकिनCat
नहीं कर सकती। इन विशिष्ट क्षमताओं को कार्यान्वयन के द्वाराCat
औरDog
जोड़ा जाता है।Man
एकAnimal
नहीं है, लेकिन वहThink
सकता है,Learn
,Apply
कर सकता है औरClimb
।Cat
एकMan
नहीं है, लेकिन यहClimb
कर सकता है।Dog
एकMan
नहीं है, लेकिन यहLearn
सकता हैMan
न तोCat
और न हीDog
लेकिनAnimal
,Cat
, याDog
विस्तार किए बिना बाद वाले दो की क्षमताओं में से कुछ हो सकता है। यह इंटरफेसेस के साथ किया जाता है।भले ही
Animal
एक अमूर्त वर्ग है, लेकिन इसमें एक इंटरफ़ेस के विपरीत एक कंस्ट्रक्टर है।
टी एल; डॉ:
असंबंधित वर्ग में इंटरफेस के माध्यम से क्षमताएं हो सकती हैं, लेकिन संबंधित वर्ग आधार कक्षाओं के विस्तार के माध्यम से व्यवहार को बदलते हैं।
समझने के लिए जावा डॉक्यूमेंटेशन पेज देखें कि किसी विशिष्ट उपयोग के मामले में किसका उपयोग करना है।
अमूर्त कक्षाओं का उपयोग करने पर विचार करें यदि ...
- आप कई निकट संबंधित वर्गों के बीच कोड साझा करना चाहते हैं।
- आप उम्मीद करते हैं कि आपके अमूर्त वर्ग का विस्तार करने वाले वर्गों के पास कई सामान्य तरीके या क्षेत्र हैं, या उन्हें सार्वजनिक (जैसे संरक्षित और निजी) के अलावा अन्य पहुँच संशोधक की आवश्यकता होती है।
- आप गैर-स्थिर या गैर-अंतिम फ़ील्ड घोषित करना चाहते हैं।
यदि इंटरफेस का उपयोग करने पर विचार करें ...
- आप उम्मीद करते हैं कि असंबंधित वर्ग आपके इंटरफ़ेस को लागू करेंगे। उदाहरण के लिए, कई असंबंधित वस्तुएँ
Serializable
इंटरफ़ेस को लागू कर सकती हैं। - आप एक विशेष डेटा प्रकार के व्यवहार को निर्दिष्ट करना चाहते हैं, लेकिन इसके व्यवहार को लागू करने के बारे में चिंतित नहीं हैं।
- आप कई प्रकार की विरासत का लाभ उठाना चाहते हैं।
वंशानुक्रम में ओवरराइडिंग
इनहेरिटेंस में ओवरराइडिंग का उपयोग तब किया जाता है जब आप सुपर क्लास से सुपर क्लास में पहले से परिभाषित पद्धति का उपयोग करते हैं, लेकिन इस तरीके से अलग तरीके से कि कैसे सुपर क्लास में मूल रूप से डिज़ाइन किया गया था। ओवरराइडिंग उपयोगकर्ता को मौजूदा सामग्री का उपयोग करके कोड का पुन: उपयोग करने की अनुमति देता है और उपयोगकर्ता की आवश्यकताओं के अनुरूप इसे बेहतर बनाता है।
निम्न उदाहरण दर्शाता है कि कैसे ClassB
की कार्यक्षमता को ओवरराइड करता है ClassA
बदलते क्या मुद्रण विधि के माध्यम से बाहर भेजा जाता है द्वारा:
उदाहरण:
public static void main(String[] args) {
ClassA a = new ClassA();
ClassA b = new ClassB();
a.printing();
b.printing();
}
class ClassA {
public void printing() {
System.out.println("A");
}
}
class ClassB extends ClassA {
public void printing() {
System.out.println("B");
}
}
आउटपुट:
ए
बी