Java Language
इंटरफेस
खोज…
परिचय
interface
कीवर्ड का उपयोग करके घोषित किया जा सकता है। इंटरफ़ेस में केवल स्थिरांक, विधि हस्ताक्षर, डिफ़ॉल्ट विधियाँ, स्थिर विधियाँ और नेस्टेड प्रकार हो सकते हैं। विधि निकाय केवल डिफ़ॉल्ट विधियों और स्थिर विधियों के लिए मौजूद हैं। अमूर्त वर्गों की तरह, इंटरफेसेस को तत्काल नहीं किया जा सकता है - उन्हें केवल कक्षाओं द्वारा लागू किया जा सकता है या अन्य इंटरफेस द्वारा विस्तारित किया जा सकता है। इंटरफ़ेस जावा में पूर्ण अमूर्तता प्राप्त करने का एक सामान्य तरीका है।
वाक्य - विन्यास
- सार्वजनिक इंटरफ़ेस फू {शून्य फ़ू (); / * किसी भी अन्य तरीके * /}
- सार्वजनिक इंटरफ़ेस Foo1 फू {void बार () को बढ़ाता है; / * किसी भी अन्य तरीके * /}
- सार्वजनिक वर्ग Foo2 लागू करता है Foo, Foo1 {/ * फू और Foo1 का कार्यान्वयन * /}
एक अंतरफलक की घोषणा और कार्यान्वयन
interface
कीवर्ड का उपयोग करके एक इंटरफेस की घोषणा:
public interface Animal {
String getSound(); // Interface methods are public by default
}
ओवरराइड एनोटेशन
@Override
public String getSound() {
// Code goes here...
}
यह संकलक को यह जांचने के लिए मजबूर करता है कि हम ओवरराइड कर रहे हैं और प्रोग्राम को नई विधि को परिभाषित करने या विधि हस्ताक्षर को गड़बड़ाने से रोकता है।
इम्प्लीमेंट्स कीवर्ड का उपयोग करके इंटरफेस को लागू implements
।
public class Cat implements Animal {
@Override
public String getSound() {
return "meow";
}
}
public class Dog implements Animal {
@Override
public String getSound() {
return "woof";
}
}
उदाहरण में, Cat
और Dog
को getSound()
विधि को परिभाषित करना चाहिए क्योंकि इंटरफ़ेस के तरीके स्वाभाविक रूप से अमूर्त हैं (डिफ़ॉल्ट विधियों के अपवाद के साथ)।
इंटरफेस का उपयोग करना
Animal cat = new Cat();
Animal dog = new Dog();
System.out.println(cat.getSound()); // prints "meow"
System.out.println(dog.getSound()); // prints "woof"
कई इंटरफेस को लागू करना
एक जावा वर्ग कई इंटरफेस को लागू कर सकता है।
public interface NoiseMaker {
String noise = "Making Noise"; // interface variables are public static final by default
String makeNoise(); //interface methods are public abstract by default
}
public interface FoodEater {
void eat(Food food);
}
public class Cat implements NoiseMaker, FoodEater {
@Override
public String makeNoise() {
return "meow";
}
@Override
public void eat(Food food) {
System.out.println("meows appreciatively");
}
}
ध्यान दें कि Cat
क्लास को दोनों इंटरफेस में विरासत में मिली abstract
विधियों को कैसे लागू करना चाहिए । इसके अलावा, ध्यान दें कि एक वर्ग व्यावहारिक रूप से आवश्यकतानुसार कई इंटरफेस को लागू कर सकता है ( जेवीएम लिमिट के कारण 65,535 की सीमा है )।
NoiseMaker noiseMaker = new Cat(); // Valid
FoodEater foodEater = new Cat(); // Valid
Cat cat = new Cat(); // valid
Cat invalid1 = new NoiseMaker(); // Invalid
Cat invalid2 = new FoodEater(); // Invalid
ध्यान दें:
- एक इंटरफ़ेस में घोषित सभी चर
public static final
- एक इंटरफ़ेस विधियों में घोषित सभी तरीके
public abstract
(यह कथन केवल जावा 7 के माध्यम से मान्य है। जावा 8 से, आपको एक इंटरफ़ेस में ऐसे तरीके रखने की अनुमति है, जिन्हें सार होने की आवश्यकता नहीं है; ऐसे तरीकों को डिफ़ॉल्ट तरीकों के रूप में जाना जाता है) - अन्तराल को
final
घोषित नहीं किया जा सकता है - यदि एक से अधिक इंटरफ़ेस एक ऐसी विधि की घोषणा करते हैं जिसमें समान हस्ताक्षर होते हैं, तो प्रभावी रूप से इसे केवल एक विधि के रूप में माना जाता है और आप उस इंटरफ़ेस से अलग नहीं हो सकते हैं जो कार्यान्वित किया गया है
- संकलन पर, प्रत्येक इंटरफ़ेस के लिए एक संबंधित InterfaceName.class फ़ाइल तैयार की जाएगी
एक इंटरफ़ेस का विस्तार
एक इंटरफेस अन्य इंटरफ़ेस विस्तार कर सकते हैं के माध्यम से extends
कीवर्ड।
public interface BasicResourceService {
Resource getResource();
}
public interface ExtendedResourceService extends BasicResourceService {
void updateResource(Resource resource);
}
अब ExtendedResourceService
को लागू करने वाले वर्ग को getResource()
और updateResource()
दोनों को लागू करना होगा।
कई इंटरफेस का विस्तार
कक्षाओं के विपरीत, extends
कीवर्ड का उपयोग नए इंटरफेस में इंटरफेस के संयोजन के लिए कई इंटरफेस (अल्पविराम द्वारा अलग) का विस्तार करने के लिए किया जा सकता है
public interface BasicResourceService {
Resource getResource();
}
public interface AlternateResourceService {
Resource getAlternateResource();
}
public interface ExtendedResourceService extends BasicResourceService, AlternateResourceService {
Resource updateResource(Resource resource);
}
इस स्थिति में ExtendedResourceService
को लागू करने वाला एक वर्ग getResource()
, getAlternateResource()
और updateResource()
लागू करने की आवश्यकता होगी।
जेनरिक के साथ इंटरफेस का उपयोग करना
मान लें कि आप एक ऐसे इंटरफ़ेस को परिभाषित करना चाहते हैं जो विभिन्न प्रकार के चैनलों (जैसे AMQP, JMS, आदि) से डेटा को प्रकाशित / उपभोग करने की अनुमति देता है, लेकिन आप कार्यान्वयन विवरण को स्विच करने में सक्षम होना चाहते हैं ...
आइए एक बुनियादी IO इंटरफ़ेस को परिभाषित करें जिसे कई कार्यान्वयनों में फिर से उपयोग किया जा सकता है:
public interface IO<IncomingType, OutgoingType> {
void publish(OutgoingType data);
IncomingType consume();
IncomingType RPCSubmit(OutgoingType data);
}
अब मैं उस इंटरफ़ेस को इंस्टेंट कर सकता हूं, लेकिन चूंकि हमारे पास उन तरीकों के लिए डिफ़ॉल्ट कार्यान्वयन नहीं है, इसलिए जब हम इसे तुरंत लागू करते हैं, तो इसे कार्यान्वयन की आवश्यकता होगी:
IO<String, String> mockIO = new IO<String, String>() {
private String channel = "somechannel";
@Override
public void publish(String data) {
System.out.println("Publishing " + data + " to " + channel);
}
@Override
public String consume() {
System.out.println("Consuming from " + channel);
return "some useful data";
}
@Override
public String RPCSubmit(String data) {
return "received " + data + " just now ";
}
};
mockIO.consume(); // prints: Consuming from somechannel
mockIO.publish("TestData"); // Publishing TestData to somechannel
System.out.println(mockIO.RPCSubmit("TestData")); // received TestData just now
हम उस इंटरफ़ेस के साथ कुछ और उपयोगी भी कर सकते हैं, मान लें कि हम इसका उपयोग कुछ मूल RabbitMQ फ़ंक्शन को लपेटने के लिए करना चाहते हैं:
public class RabbitMQ implements IO<String, String> {
private String exchange;
private String queue;
public RabbitMQ(String exchange, String queue){
this.exchange = exchange;
this.queue = queue;
}
@Override
public void publish(String data) {
rabbit.basicPublish(exchange, queue, data.getBytes());
}
@Override
public String consume() {
return rabbit.basicConsume(exchange, queue);
}
@Override
public String RPCSubmit(String data) {
return rabbit.rpcPublish(exchange, queue, data);
}
}
मान लें कि मैं इस IO इंटरफ़ेस का उपयोग अब मेरी वेबसाइट पर आने वाली यात्राओं को गिनने के तरीके के रूप में करना चाहता हूं क्योंकि मेरी अंतिम प्रणाली फिर से शुरू हो गई है और फिर विज़िट की कुल संख्या प्रदर्शित करने में सक्षम हैं - आप इस तरह से कुछ कर सकते हैं:
import java.util.concurrent.atomic.AtomicLong;
public class VisitCounter implements IO<Long, Integer> {
private static AtomicLong websiteCounter = new AtomicLong(0);
@Override
public void publish(Integer count) {
websiteCounter.addAndGet(count);
}
@Override
public Long consume() {
return websiteCounter.get();
}
@Override
public Long RPCSubmit(Integer count) {
return websiteCounter.addAndGet(count);
}
}
अब चलिए VisitCounter का उपयोग करते हैं:
VisitCounter counter = new VisitCounter();
// just had 4 visits, yay
counter.publish(4);
// just had another visit, yay
counter.publish(1);
// get data for stats counter
System.out.println(counter.consume()); // prints 5
// show data for stats counter page, but include that as a page view
System.out.println(counter.RPCSubmit(1)); // prints 6
कई इंटरफेस लागू करते समय, आप एक ही इंटरफ़ेस को दो बार लागू नहीं कर सकते। यह जेनेरिक इंटरफेस पर भी लागू होता है। इस प्रकार, निम्नलिखित कोड अमान्य है, और एक संकलित त्रुटि होगी:
interface Printer<T> {
void print(T value);
}
// Invalid!
class SystemPrinter implements Printer<Double>, Printer<Integer> {
@Override public void print(Double d){ System.out.println("Decimal: " + d); }
@Override public void print(Integer i){ System.out.println("Discrete: " + i); }
}
इंटरफेस की उपयोगिता
कई मामलों में इंटरफेस बेहद मददगार हो सकते हैं। उदाहरण के लिए, मान लें कि आपके पास जानवरों की एक सूची है और आप सूची के माध्यम से लूप करना चाहते हैं, प्रत्येक ध्वनि जो वे बनाते हैं, उसे मुद्रित करते हैं।
{cat, dog, bird}
ऐसा करने का एक तरीका इंटरफेस का उपयोग करना होगा। यह सभी वर्गों के लिए एक ही विधि के लिए अनुमति देता है
public interface Animal {
public String getSound();
}
कोई भी वर्ग जो implements Animal
करता है, उनके पास एक getSound()
विधि होनी चाहिए, फिर भी वे सभी अलग-अलग कार्यान्वयन कर सकते हैं
public class Dog implements Animal {
public String getSound() {
return "Woof";
}
}
public class Cat implements Animal {
public String getSound() {
return "Meow";
}
}
public class Bird implements Animal{
public String getSound() {
return "Chirp";
}
}
अब हमारे पास तीन अलग-अलग कक्षाएं हैं, जिनमें से प्रत्येक में getSound()
विधि है। क्योंकि ये सभी वर्ग Animal
इंटरफ़ेस को implement
करते हैं, जो getSound()
विधि को घोषित करता है, किसी भी Animal
उदाहरण में getSound()
सकता है।
Animal dog = new Dog();
Animal cat = new Cat();
Animal bird = new Bird();
dog.getSound(); // "Woof"
cat.getSound(); // "Meow"
bird.getSound(); // "Chirp"
क्योंकि इनमें से प्रत्येक एक Animal
, हम जानवरों को एक सूची में रख सकते हैं, उनके माध्यम से लूप कर सकते हैं, और उनकी आवाज़ निकाल सकते हैं
Animal[] animals = { new Dog(), new Cat(), new Bird() };
for (Animal animal : animals) {
System.out.println(animal.getSound());
}
क्योंकि सरणी का क्रम Dog
, Cat
, और फिर Bird
, "वूप मेव चिरप" कंसोल पर मुद्रित किया जाएगा।
कार्यों के लिए वापसी मूल्य के रूप में इंटरफेस का भी उपयोग किया जा सकता है। उदाहरण के लिए, एक Dog
लौटाना यदि इनपुट "कुत्ता" है , तो Cat
यदि "बिल्ली" है , और Bird
यदि वह "पक्षी" है , और फिर उस जानवर की ध्वनि को प्रिंट करके उपयोग किया जा सकता है
public Animal getAnimalByName(String name) {
switch(name.toLowerCase()) {
case "dog":
return new Dog();
case "cat":
return new Cat();
case "bird":
return new Bird();
default:
return null;
}
}
public String getAnimalSoundByName(String name){
Animal animal = getAnimalByName(name);
if (animal == null) {
return null;
} else {
return animal.getSound();
}
}
String dogSound = getAnimalSoundByName("dog"); // "Woof"
String catSound = getAnimalSoundByName("cat"); // "Meow"
String birdSound = getAnimalSoundByName("bird"); // "Chirp"
String lightbulbSound = getAnimalSoundByName("lightbulb"); // null
इंटरफेस भी एक्स्टेंसिबिलिटी के लिए उपयोगी होते हैं, क्योंकि यदि आप एक नए प्रकार के Animal
को जोड़ना चाहते हैं, तो आपको उन पर किए जाने वाले ऑपरेशन के साथ कुछ भी बदलने की आवश्यकता नहीं होगी।
एक अमूर्त वर्ग में इंटरफेस को लागू करना
interface
में परिभाषित एक विधि डिफ़ॉल्ट public abstract
द्वारा है। जब एक abstract class
एक interface
लागू करता interface
, तो interface
में परिभाषित किसी भी तरीके को abstract class
द्वारा लागू नहीं किया जाना है। ऐसा इसलिए है क्योंकि एक class
जिसे abstract
घोषित किया गया है, उसमें अमूर्त विधि घोषणाएँ हो सकती हैं। इसलिए किसी भी इंटरफेस और / या abstract class
से विरासत में मिली किसी भी abstract
विधियों को लागू करना पहली ठोस उप-वर्ग की जिम्मेदारी है।
public interface NoiseMaker {
void makeNoise();
}
public abstract class Animal implements NoiseMaker {
//Does not need to declare or implement makeNoise()
public abstract void eat();
}
//Because Dog is concrete, it must define both makeNoise() and eat()
public class Dog extends Animal {
@Override
public void makeNoise() {
System.out.println("Borf borf");
}
@Override
public void eat() {
System.out.println("Dog eats some kibble.");
}
}
जावा 8 के बाद से एक interface
लिए तरीकों के default
कार्यान्वयन की घोषणा करना संभव है, जिसका अर्थ है कि विधि abstract
नहीं होगी, इसलिए किसी भी ठोस उप-वर्गों को विधि को लागू करने के लिए मजबूर नहीं किया जाएगा, लेकिन जब तक ओवरराइड नहीं किया जाता है तब तक default
कार्यान्वयन विरासत में मिलेगा।
डिफ़ॉल्ट तरीके
जावा 8 में पेश किया गया, डिफ़ॉल्ट तरीके एक इंटरफ़ेस के अंदर कार्यान्वयन को निर्दिष्ट करने का एक तरीका है। इसका उपयोग इंटरफ़ेस के आंशिक कार्यान्वयन प्रदान करके और उपवर्गों के पदानुक्रम को सीमित करके विशिष्ट "आधार" या "सार" वर्ग से बचने के लिए किया जा सकता है।
ऑब्जर्वर पैटर्न कार्यान्वयन
उदाहरण के लिए, प्रेक्षक-श्रोता पैटर्न को सीधे इंटरफ़ेस में लागू करना संभव है, कार्यान्वयन कक्षाओं को अधिक लचीलापन प्रदान करता है।
interface Observer {
void onAction(String a);
}
interface Observable{
public abstract List<Observer> getObservers();
public default void addObserver(Observer o){
getObservers().add(o);
}
public default void notify(String something ){
for( Observer l : getObservers() ){
l.onAction(something);
}
}
}
अब, किसी भी वर्ग को एक अलग वर्ग पदानुक्रम का हिस्सा होने के लिए स्वतंत्र होने के दौरान, केवल ऑब्जर्वेबल इंटरफ़ेस को लागू करके "ऑब्जर्वेबल" बनाया जा सकता है।
abstract class Worker{
public abstract void work();
}
public class MyWorker extends Worker implements Observable {
private List<Observer> myObservers = new ArrayList<Observer>();
@Override
public List<Observer> getObservers() {
return myObservers;
}
@Override
public void work(){
notify("Started work");
// Code goes here...
notify("Completed work");
}
public static void main(String[] args) {
MyWorker w = new MyWorker();
w.addListener(new Observer() {
@Override
public void onAction(String a) {
System.out.println(a + " (" + new Date() + ")");
}
});
w.work();
}
}
हीरे की समस्या
जावा 8 में संकलक हीरे की समस्या से अवगत है जो तब होता है जब एक वर्ग उसी हस्ताक्षर के साथ एक विधि को लागू करने वाले इंटरफेस को लागू कर रहा है।
इसे हल करने के लिए, एक कार्यान्वयन वर्ग को साझा विधि को ओवरराइड करना चाहिए और अपना स्वयं का कार्यान्वयन प्रदान करना चाहिए।
interface InterfaceA {
public default String getName(){
return "a";
}
}
interface InterfaceB {
public default String getName(){
return "b";
}
}
public class ImpClass implements InterfaceA, InterfaceB {
@Override
public String getName() {
//Must provide its own implementation
return InterfaceA.super.getName() + InterfaceB.super.getName();
}
public static void main(String[] args) {
ImpClass c = new ImpClass();
System.out.println( c.getName() ); // Prints "ab"
System.out.println( ((InterfaceA)c).getName() ); // Prints "ab"
System.out.println( ((InterfaceB)c).getName() ); // Prints "ab"
}
}
अभी भी एक ही नाम और अलग-अलग रिटर्न प्रकार के मापदंडों के साथ तरीके होने का मुद्दा है, जो संकलन नहीं करेगा।
संगतता समस्याओं को हल करने के लिए डिफ़ॉल्ट विधियों का उपयोग करें
यदि किसी मौजूदा सिस्टम में इंटरफ़ेस को कई वर्गों द्वारा उपयोग किया जाता है, तो एक विधि को इंटरफ़ेस में जोड़ा जाता है, तो डिफ़ॉल्ट विधि कार्यान्वयन बहुत काम आता है।
संपूर्ण सिस्टम को तोड़ने से बचने के लिए, आप एक इंटरफ़ेस में एक विधि जोड़ते समय एक डिफ़ॉल्ट विधि कार्यान्वयन प्रदान कर सकते हैं। इस तरह, सिस्टम अभी भी संकलित होगा और वास्तविक कार्यान्वयन को चरण दर चरण किया जा सकता है।
अधिक जानकारी के लिए, डिफ़ॉल्ट विधियाँ विषय देखें।
इंटरफेसेस में संशोधक
ओरेकल जावा स्टाइल गाइड स्टेट्स:
संशोधित किए जाने पर संशोधक को बाहर नहीं लिखा जाना चाहिए।
(संदर्भ के लिए ओरेकल आधिकारिक कोड मानक में संशोधक देखें और वास्तविक ओरेकल दस्तावेज़ के लिए एक लिंक।)
यह शैली मार्गदर्शन विशेष रूप से इंटरफेस पर लागू होता है। आइए निम्नलिखित कोड स्निपेट पर विचार करें:
interface I {
public static final int VARIABLE = 0;
public abstract void method();
public static void staticMethod() { ... }
public default void defaultMethod() { ... }
}
चर
सभी इंटरफ़ेस चर निहित के साथ परोक्ष स्थिरांक हैं public
(सभी के लिए सुलभ), static
और (इंटरफ़ेस नाम से जाया जा सकता है) final
(घोषणा के दौरान प्रारंभ किया जाना चाहिए) संशोधक:
public static final int VARIABLE = 0;
तरीके
- सभी तरीके जो कार्यान्वयन प्रदान नहीं करते हैं, वे
public
औरabstract
।
public abstract void method();
-
static
याdefault
संशोधक वाली सभी विधियों को कार्यान्वयन प्रदान करना चाहिए और यह स्पष्ट रूप सेpublic
होना चाहिए ।
public static void staticMethod() { ... }
उपरोक्त सभी परिवर्तनों को लागू करने के बाद, हम निम्नलिखित प्राप्त करेंगे:
interface I {
int VARIABLE = 0;
void method();
static void staticMethod() { ... }
default void defaultMethod() { ... }
}
बंधे हुए प्रकार के मापदंडों को मजबूत करें
बाध्य प्रकार पैरामीटर आपको सामान्य प्रकार के तर्कों पर प्रतिबंध लगाने की अनुमति देते हैं:
class SomeClass {
}
class Demo<T extends SomeClass> {
}
लेकिन एक प्रकार का पैरामीटर केवल एकल वर्ग प्रकार के लिए बाध्य हो सकता है।
एक इंटरफ़ेस प्रकार एक प्रकार से बाध्य हो सकता है जिसमें पहले से ही एक बंधन था। यह &
सिंबल का उपयोग करके हासिल किया गया है:
interface SomeInterface {
}
class GenericClass<T extends SomeClass & SomeInterface> {
}
यह कई प्रकारों से प्राप्त करने के लिए संभावित रूप से आवश्यक तर्क को मजबूत करता है।
एकाधिक इंटरफ़ेस प्रकार एक पैरामीटर के लिए बाध्य हो सकते हैं:
class Demo<T extends SomeClass & FirstInterface & SecondInterface> {
}
लेकिन सावधानी के साथ इस्तेमाल किया जाना चाहिए। एकाधिक इंटरफ़ेस बाइंडिंग आमतौर पर एक कोड गंध का संकेत है, यह सुझाव देता है कि एक नया प्रकार बनाया जाना चाहिए जो अन्य एडाप्टर के लिए एक एडाप्टर के रूप में कार्य करता है:
interface NewInterface extends FirstInterface, SecondInterface {
}
class Demo<T extends SomeClass & NewInterface> {
}