وراثت
وراثت به یک کلاس اجازه میدهد که خصوصیات یا متدهایی را از کلاس دیگر به ارث برد. وراثت مانند رابطه پدر و پسری میماند به طوریکه فرزند خصوصیاتی از قبیل قیافه و رفتار را از پدر خود به ارث برده باشد.
همه متد و خصوصیات کلاس پایه میتوانند در کلاس مشتق مورد استفاده قرار بگیرند به استثنای اعضاء و متدهای با سطح دسترسی private. مفهوم اصلی وراثت در مثال زیر نشان داده شده است :
1: class Parent 2: { 3: private var message: String 4: 5: var Message: String 6: { 7: get 8: { 9: return message 10: } 11: set 12: { 13: message = newValue 14: } 15: } 16: 17: init(message: String) 18: { 19: self.message = message 20: } 21: 22: func ShowMessage() 23: { 24: print(message) 25: } 26: } 27: 28: class Child : Parent 29: { 30: override init(message: String) 31: { 32: super.init(message: message) 33: } 34: } 35: 36: var myParent = Parent(message: "Message from parent.") 37: var myChild = Child (message: "Message from child.") 38: 39: myParent.ShowMessage() 40: myChild.ShowMessage() 41: 42: myParent.Message = "Modified message of the parent." 43: myParent.ShowMessage() 44: 45: myChild.Message = "Modified message of the child." 46: myChild.ShowMessage()
Message from parent. Message from child. Modified message of the parent. Modified message of the child.
در این مثال دو کلاس با نامهای Parent و Child تعریف شده است. در این مثال یک فیلد با سطح دسترسی private (خط 3) و خاصیت مربوط به آن را در خطوط 15-5 تعریف کردهایم. ابن کلاس دارای یک متد برای نمایش پیام میباشد (خطوط 25-22). یک سازنده در کلاس Parent تعریف شده است که یک آرگومان از نوع رشته قبول میکند و یک پیغام نمایش میدهد (خطوط 20-17). حال به کلاس Child توجه کنید (خط 34-28). این کلاس تمام متدها و خاصیتهای کلاس Parent را به ارث برده است. نحوه ارث بری یک کلاس به صورت زیر است :
class DerivedClass : BaseClass
براحتی میتوان با قرار دادن یک کالن ( : ) بعد از نام کلاس و سپس نوشتن نام کلاسی که از آن ارث بری میشود (کلاس پایه) این کار را انجام داد (خط 28). در داخل کلاس Child هم یک سازنده ساده وجود دارد که یک آرگومان رشتهای قبول میکند (خط 33-30). قبل از توضیح جزیی تر کد، ابتدا باید دو کلمه کلیدی override و super را توضیح دهیم.
override و super
فرض کنید شما تابع A را در کلاس A دارید و کلاس B از کلاس A ارث بری میکند، در این صورت تابع A در کلاس B در دسترس خواهد بود. اما تابع A دقیق همان تابعی است که از کلاس A به ارث برده شده است. حال اگر بخواهید که این تابع رفتار متفاوتی از خود نشان دهد چکار میکنید؟ برای حل این مشکل باید تابع کلاس پایه را Override کنید. به تکه کد زیر توجه کنید:
1: class Parent 2: { 3: func ShowMessage() 4: { 5: print("Message from Parent.") 6: } 7: } 8: 9: class Child : Parent 10: { 11: override func ShowMessage() 12: { 13: print("Message from Child.") 14: } 15: } 16: 17: var myParent = Parent() 18: var myChild = Child() 19: 20: myParent.ShowMessage() 21: myChild.ShowMessage()
Message from Parent. Message from Child.
همانطور که در کد بالا مشاهده میکنید دو کلاس به نام Parent (خطوط 7-1) و Child (خطوط 15-9) تعریف کردهایم. کلاس Child که از کلاس Parent ارث میبرد شامل تابعی است که تابع ()ShowMessage از کلاس پایه را override یا به صورت دیگری پیاده سازی میکند. همانطور که مشاهده میکنید این دو تابع دقیقاً شبیه به هم هستند و تنها اختلاف آنها در پیامی است که نشان میدهند. با استفاده از کلمه کلیدی super (خط 13 کد زیر) میتوانید تابع کلاس پایه را در داخل تابع override شده فراخوانی کنید:
1: class Parent 2: { 3: func ShowMessage() 4: { 5: print("Message from Parent.") 6: } 7: } 8: 9: class Child : Parent 10: { 11: override func ShowMessage() 12: { 13: super.ShowMessage() 14: print("Message from Child.") 15: } 16: } 17: 18: var myParent = Parent() 19: var myChild = Child() 20: 21: myParent.ShowMessage() 22: myChild.ShowMessage()
Message from Parent. Message from Parent. Message from Child.
میتوان یک کلاس دیگر که از کلاس Child ارث بری میکند ایجاد کرده و دوباره تابع ShowMessage() را override کرده و آنرا به صورت دیگر پیاده سازی کنیم. اگر بخواهید تابعی را که ایجاد کردهاید به وسیله سایر کلاسها override نشود کافیست که از کلمه کلیدی final به صورت زیر استفاده کنید:
final func ShowMessage()
حال اگر کلاس دیگری از کلاس Child ارث ببرد نمیتواند تابع ShowMessage() را override کند. به کد ابتدای درس بر میگردیم. وقتی از وراثت در کلاسها استفاده میکنیم، هم سازنده کلاس مشتق و هم سازنده پیشفرض کلاس پایه هر دو اجرا میشوند. سازنده پیشفرض یک سازنده بدون پارامتر است. اگر برای یک کلاس سازندهای تعریف نکنیم کامپایلر به صورت خودکار یک سازنده برای آن ایجاد میکند. اگر هنگام صدا زدن سازنده کلاس مشتق بخواهیم سازنده کلاس پایه را صدا بزنیم باید از کلمه کلیدی super استفاده کنیم. کلمه کلیدی super یک متد از کلاس پایه را صدا می زند.
در مثال ابتدای درس به وسیله تأمین مقدار پارامتر message سازنده کلاس مشتق و ارسال آن به داخل پرانتز سازنده کلاس پدر که با کلمه کلیدی super آن را فراخوانی کرده ایم، سازنده معادل آن در کلاس پایه فراخوانی شده و مقدار message را به آن ارسال میکند. سازنده کلاس Parent هم این مقدار (مقدار message) را در فیلد private قرار میدهد. میتوانید کدهایی را به داخل بدنه سازنده Child اضافه کنید تا بعد از سازنده Parent اجرا شوند. اگر از کلمه کلیدی super استفاده نشود به جای کلاس پایه، سازنده پیشفرض فراخوانی میشود.
اجازه بدهید که اشیایی از کلاسهای Parent و Child بسازیم تا نشان دهیم که چگونه کلاس Child متدها و خواص کلاس Parent را به ارث میبرد. هر دو شیء را با استفاده از سازندههای مربوط به خودشان مقدار دهی میکنیم (خطوط 37-36). از آنجاییکه کلاس Child از کلاس Parent ارث بری کرده است و سازنده آن را فراخوانی نموده، در نتیجه وقتی به سازنده کلاس Child یک مقدار اختصاص میدهیم این مقدار از طریق سازنده کلاس Parent به فیلد message در خط 3 اختصاص داده میشود و در نهایت توسط متد ()ShowMessage نمایش داده میشود. حال در خط 40 با فراخوانی این متد متنی که در سازنده کلاس Child نوشتهایم، نمایش داده میشود. حتی اگر کلاس Child از کلاس Parent ارث ببرد باز هم اعضای با سطح دسترسی private در کلاس Child قابل دسترسی نیستند. به نکته دیگر توجه کنید. اگر کلاس دیگری بخواهد از کلاس Child ارث بری کند، باز هم تمام متدها و خواص کلاس Child که از کلاس Parent به ارث برده است را به ارث میبرد:
class GrandChild : Child { //Empty Body }
این کلاس هیچ چیزی در داخل بدنه ندارد. وقتی کلاس GrandChild را ایجاد میکنید و یک خاصیت از کلاس Parent را فراخوانی میکنید با خطا مواجه میشوید. چون هیچ سازندهای که یک آرگومان رشتهای قبول کند در داخل بدنه GrandChild تعریف نشده است بنابراین شما میتوانید فقط از سازنده پیشفرض یا بدون پارامتر استفاده کنید.
var myGrandChild = GrandChild() myGrandChild.Message = "Hello my grandchild!" myGrandChild.ShowMessage()
وقتی یک کلاس ایجاد میکنیم و سازنده GrandChild را فراخوانی میکنیم ابتدا سازنده کلاس Parent فراخوانی میشود و سپس سازنده Child و در نهایت سازنده GrandChild اجرا میشود. برنامه زیر ترتیب اجرای سازندهها را نشان میدهد:
1: class Parent 2: { 3: init() 4: { 5: print("Parent constructor was called!") 6: } 7: } 8: 9: class Child : Parent 10: { 11: override init() 12: { 13: print("Child constructor was called!") 14: } 15: } 16: 17: class GrandChild : Child 18: { 19: override init() 20: { 21: print("GrandChild constructor was called!") 22: } 23: } 24: 25: var myGrandChild = GrandChild()
Parent constructor was called! Child constructor was called! GrandChild constructor was called!
با آنکه در خط 25 ما یک نمونه از کلاس GrandChild ایجاد کرده ایم ولی چون این کلاس از کلاس Child و کلاس Child از کلاس Parent ارث بری کرده است، در نتیجه، کلاس GrandChild، دارای تمام متدهای سازنده دو کلاس مذکور هم می باشد.