رابط (Interface)

اینترفیس ها شبیه به کلاسها هستند اما فقط شامل تعاریفی برای متدها و خواص (Property) می باشند. اینترفیس ها را می توان به عنوان پلاگین های کلاس ها در نظر گرفت. کلاسی که یک اینترفیس خاص را پیاده سازی می کند لازم است که کدهایی برای اجراتوسط اعضا و متدهای آن فراهم کند چون اعضا و متدهای اینترفیس هیچ کد اجرایی در بدنه خود ندارند. اجازه دهید که نحوه تعریف و استفاده از یک اینترفیس در کلاس را توضیح دهیم :

   1: package myfirstprogram;
   2: 
   3: interface ISample                            
   4: {                                            
   5:     public void ShowMessage(String message); 
   6: }                                            
   7:
   8: class Sample implements ISample              
   9: {  
  10:     public void ShowMessage(String message)  
  11:     {                                        
  12:         System.out.println(message);         
  13:     }                                        
  14: } 
  15: 
  16: public class MyFirstProgram 
  17: {
  18:     public static void main(String[] args) 
  19:     {        
  20:         Sample sample = new Sample();                            
  21: 
  22:         sample.ShowMessage("Implemented the ISample Interface!");
  23:     }    
  24: }
Implemented the ISample Interface!

در خطوط 6-3 یک اینترفیس به نام ISample تعریف کرده ایم. بر طبق قراردادهای نامگذاری، اینترفیس ها به شیوه پاسکال نامگذاری می شوند و همه آنها باید با حرف I شروع شوند. همچنین در تعریف آنها باید از کلمه کلیدی interface استفاده شود. یک متد در داخل بدنه اینترفیس تعریف می کنیم (خط 5). به این نکته توجه کنید که متد تعریف شده فاقد بدنه است و در آخر ان باید از سیمیکولن استفاده شود.
وقتی که متد را در داخل اینترفیس تعریف می کنید فقط لازم است که عنوان متد (نوع ، نام و پارامترهای آن) را بنویسید. به این نکته نیز توجه کنید که متدها و خواص تعریف شده در داخل اینترفیس سطح دسترسی ندارند چون باید همیشه هنگام اجرای کلاسها در دسترس باشند. برای پیاده سازی یک interface توسط یک کلاس از کلمه کلیدی implements استفاده می شود. کلاسی که اینترفیس را اجرا می کند کدهای واقعی را برای اعضای آن فراهم می کند. همانطور که در مثال بالا می بینید کلاس Sample ، متد ShowMessage() اینترفیس ISample را اجرا و تغذیه می کند. برای روشن شدن کاربرد رابط ها به مثال زیر توجه کنید:

   1: package myfirstprogram;
   2: 
   3: import java.text.MessageFormat;
   4: 
   5: class CA
   6: {
   7:     public String FullName;
   8:     public int Age;
   9: 
  10:     public CA(String fullname, int age)
  11:     {
  12:         this.FullName = fullname;
  13:         this.Age = age;
  14:     }
  15: }
  16: 
  17: class CB
  18: {
  19:     public String FirstName;
  20:     public String LastName;
  21:     public double PersonsAge;
  22: 
  23:     public CB(String firstname, String lastname, int personage)
  24:     {
  25:         this.FirstName = firstname;
  26:         this.LastName = lastname;
  27:         this.PersonsAge = personage;
  28:     }
  29: }
  30: 
  31: public class MyFirstProgram
  32: {
  33:     static void PrintInfo(CA item)
  34:     {
  35:         System.out.println(MessageFormat.format("Name: {0}, Age {1}", item.FullName, item.Age));
  36:     }
  37:     public static void main(String[] args)
  38:     {
  39:         CA a = new CA("John Doe", 35);
  40: 
  41:         PrintInfo(a);
  42:     }
  43: }

در کد بالا دو کلاس CA و CB تعریف شده اند، در کلاس CA دو فیلد به نام FullName و Age و در کلاس CB سه فیلد به نام های FirstName و LastName ،PersonsAge تعریف کرده ایم. در کلاس Program یک متد به نام ()PrintInfo داریم که یک پارامتر از نوع کلاس CA دارد. به شکل ساده در این متد مقدار فیلد های شی ای که به این متد ارسال شده است چاپ می شود. در متد Main یک شی از کلاس CA ساخته ایم و فیلد های آن را مقدار دهی کرده ایم. سپس این شی را به متد ()PrintInfo ارسال می کنیم. کلاس های CA و CB از نظر مفهومی شبیه یکدیگر هستند مثلا کلاس CA فیلد FullName را برای نمایش نام و نام خانوادگی دارد ولی کلاس CB برای نمایش نام و نام خانوادگی دو فیلد جدا از هم به نام های FirstName و LastName را دارد. و همچنین یک فیلد برای نگهداری مقدار سن داریم که در کلاس CA نام آن Age و در کلاس CB نام آن PersonAge می باشد. مشکل اینجاست که اگر ما یک شی از کلاس CA را به متد (()PrintInfo) ارسال کنیم از آنجایی که در داخل بدنه این متد فقط مقدار دو فیلد چاپ می شود اگر بخواهیم یک شی از کلاس CB را به آن ارسال کنیم که دارای سه فیلد است با خطا مواجه می شویم (زیرا متد ()PrintInfo با ساختار کلاس CA سازگار است و فیلد های CB را نمی شناسد). برای رفع این مشکل باید ساختار دو کلاس CA و CB را شبیه هم کنیم و این کار را با استفاده از Interface انجام می دهیم.

   1: package myfirstprogram;
   2: 
   3: import java.text.MessageFormat;
   4: 
   5: interface IInfo      
   6: {                    
   7:     String GetName();
   8:     String GetAge(); 
   9: }                    
  10: 
  11: class CA implements IInfo
  12: {
  13:     public String FullName;
  14:     public int Age;
  15: 
  16:     public CA(String fullname, int age)
  17:     {
  18:         this.FullName = fullname;
  19:         this.Age = age;
  20:     }
  21: 
  22:     @Override                                             
  23:     public String GetName() { return FullName; }          
  24:     @Override                                             
  25:     public String GetAge() { return String.valueOf(Age); }
  26: }
  27: 
  28: class CB implements IInfo
  29: {
  30:     public String FirstName;
  31:     public String LastName;
  32:     public int PersonsAge;
  33: 
  34:     public CB(String firstname, String lastname, int personage)
  35:     {
  36:         this.FirstName = firstname;
  37:         this.LastName = lastname;
  38:         this.PersonsAge = personage;
  39:     }
  40: 
  41:     @Override                                                     
  42:     public String GetName() { return FirstName + " " + LastName; }
  43:     @Override                                                     
  44:     public String GetAge() { return String.valueOf(PersonsAge); } 
  45: }
  46: 
  47: public class MyFirstProgram
  48: {
  49:     static void PrintInfo(IInfo item)
  50:     {
  51:         System.out.println(MessageFormat.format("Name: {0}, Age {1}", item.GetName(), item.GetAge()));
  52:     }
  53: 
  54:     public static void main(String[] args)
  55:     {
  56:         CA a = new CA("John Doe", 35);
  57:         CB b = new CB("Jane", "Doe", 33);
  58: 
  59:         PrintInfo(a);
  60:         PrintInfo(b);
  61:     }
  62: }
Name: John Doe, Age 35
Name: Jane Doe, Age 33

کد بالا را می توان به اینصورت توضیح داد که در خط 9-5 یک رابط به نام IInfo تعریف و آن را در خطوط 11 و 28 توسط دو کلاس CA و CB پیاده سازی کرده ایم. چون این دو کلاس وظیف دارند متدهای این رابط را پیاده سازی کنند پس در خطوط 25-22 و 45-41 کدهای بدنه دو متد این رابط را آن طور که می خواهیم، می نویسیم. در خط 49 متد ()PrintInfo را طوری دستکاری می کنیم که یک پارامتر از نوع رابط دریافت کند. حال زمانی که دو شیء از دو کلاس CA و CB در دو خط 56 و 57 ایجاد می کنیم و آنها را در دو خط 60 و 59 به متد ()PrintInfo ارسال می کنیم، چونکه این دو کلاس رابط IInfo را پیاده سازی کرده اند به طور صریح به رابط تبدیل می شود. یعنی کلاسی که یک رابط را پیاده سازی کند به طور صریح می تواند به رابط تبدیل شود. حال بسته به اینکه شیء کدام کلاس به متد ()PrintInfo ارسال شده است، متد مربوط به آن کلاس فراخوانی شده و مقادیر فیلدها چاپ می شود. می توان چند اینترفیس را در کلاس اجرا کرد.

class Sample implements ISample1, ISample2, ISample3
{
   //Implement all interfaces
}

به مثال زیر توجه کنید :

   1: package myfirstprogram;
   2: 
   3: interface IFirstinterface                     
   4: {                                                       
   5:     public void FirstMessage(String message); 
   6: }                                             
   7: 
   8: interface ISecondinterface                    
   9: {                                                       
  10:     public void SecondMessage(String message);
  11: }                                             
  12: 
  13:                                                                  
  14: class Sample implements IFirstinterface , ISecondinterface                   
  15: {                                                                
  16:     @Override
  17:     public void FirstMessage(String message)                      
  18:     {                                                            
  19:         System.out.println(message);                              
  20:     }
  21:     
  22:     @Override
  23:     public void SecondMessage(String message)                      
  24:     {                                                            
  25:         System.out.println(message);                              
  26:     }
  27: } 
  28: 
  29: public class MyFirstProgram 
  30: {
  31:     public static void main(String[] args) 
  32:     {        
  33:         Sample sample = new Sample();                            
  34:                                                                  
  35:         sample.FirstMessage("Implemented the IFirstinterface Interface!");
  36:         sample.SecondMessage("Implemented the ISecondinterface Interface!");
  37:     }    
  38: }
Implemented the IFirstinterface Interface!
Implemented the ISecondinterface Interface!

درست است که می توان از چند اینترفیس در کلاس استفاده کرد ولی باید مطمئن شد که کلاس می تواند همه اعضای اینترفیسها را تغذیه کند. همانطور که در کد بالا مشاهده می کنید دو اینترفیس به نام های IFirstinterface و ISecondinterface در خطوط 11-3 تعریف شده اند که به ترتیب دارای دو متد به نام های ()FirstMessage و ()SecondMessage می باشند. در خط 14 کلاس Sample این دو اینترفیس را پیاده سازی کرده است و درنتیجه همانطور که اشاره شد لازم است که کدهای بدنه دو متد موجود در این دو رابط را تغذیه کند. که این کار در خطوط 26-17 انجام شده است. عبارت Override@ موجود در خطوط 16 و 22 به این دلیل قرار داده شده است که به کامپایلر اعلام کند که این دو متد Override شده اند و به نوعی یک اخطار به کامپایلر هست که می گوید این تایع مربوط به کلاس یا اینترفیسی است که کلاس جاری از آن مشتق شده و یا پیاده سازی کرده است.وجود @ هم به خاطر قرار داد خود زبان می باشد. اگر یک کلاس از کلاس پایه ارث ببرد و در عین حال از اینترفیس ها هم استفاده کند، در این صورت باید نام کلاس پایه قبل از نام اینترفیس ها ذکر شود. به شکل زیر :

class Sample extends BaseClass, ISample1, ISample2
{
}

می توان از یک اینترفیس یک متغیر ایجاد کرد :

ISample sample;

ولی نمی توان از یک اینترفیس نمونه ای ایجاد کرد، چون اینترفیس ها دارای سازنده نیستند، مثلا کد زیر اشتباه است :

ISample sample = new ISample();

حال فرض کنید که ما یک متغیر از یک اینترفیس ایجاد کردیم. حال چه چیزی را می توانیم به این متغیر اختصاص دهیم و کاربرد آن چیست؟ بعد از ایجاد متغیر می توان یک شیء از کلاسی که اینترفیس را پیاده سازی کرده است به آن اختصاص دهیم :

ISample sample = new SampleClass();

برای درک کاربرد آن نیز به مثال زیر توجه کنید :

   1: package myfirstprogram;
   2: 
   3: interface IBase
   4: {
   5:     void BaseMethod();
   6: }
   7: 
   8: class Sample implements IBase
   9: {
  10:     @Override
  11:     public void BaseMethod()
  12:     {
  13:         System.out.println("Hello World");
  14:     }
  15:  }
  16:  
  17: public class MyFirstProgram
  18: {
  19:     public static void main(String[] args)
  20:     {
  21:         IBase base = new Sample();
  22:         base.BaseMethod();
  23:     }
  24: }
Hello World

همانطور که در کد بالا مشاهده می کنید در خط 8 رابط IBase توسط کلاس Sample پیاده سازی شده است. در نتیجه در خط 21 می توانیم یک شیء از کلاس Sample را به این رابط اختصاص دهیم. در خط 22 هم می توانیم با شیء ایجاد شده از این رابط و سپس گذاشتن علامت نقطه به متد ()BaseMethod دسترسی یابیم.

اینترفیسها حتی می توانند از اینترفیسهای دیگر با استفاده از کلمه کلیدی extends ارث بری کنند. به مثال زیر توجه کنید :

   1: package myfirstprogram;
   2: 
   3: interface IBase
   4: {
   5:     void BaseMethod();
   6: }
   7: 
   8: interface ISample extends IBase
   9: {
  10:     void ShowMessage(String message);
  11: }
  12: 
  13: class Sample implements ISample
  14: {
  15:     @Override
  16:     public void ShowMessage(String message)
  17:     {
  18:         System.out.println(message);
  19:     }
  20: 
  21:     @Override
  22:     public void BaseMethod()
  23:     {
  24:         System.out.println("Method from base interface!");
  25:     }
  26: }
  27: 
  28: public class MyFirstProgram 
  29: {
  30:     public static void main(String[] args) 
  31:     { 
  32:         Sample sample = new Sample();
  33: 
  34:         sample.ShowMessage("Implemented the ISample Interface!");
  35:         sample.BaseMethod();
  36:     }
  37: }
Implemented the ISample Interface!
Method from base interface!

همانطور که در خط 8 کد بالا مشاهده می کنید رابط ISample از رابط IBase ارث بری کرده است پس حتی اگر کلاس Sample فقط اینترفیس ISample را پیاده سازی کند، لازم است که همه اعضای IBase را هم پیاده سازی کند چون ISample از آن ارث بری می کند.