وراثت (inheritance) در کاتلین

وراثت در کاتلین

توضیحات

شی گرایی در برنامه نویسی به شما این امکان رو میده ویژگی ها و توابع یک کلاس رو به کلاس جدید منتقل کنید به این قابلیت وراثت یا ارث بری (inheritance) گفته میشه

قابلیت وراثت یا ارث بری کمک میکنه تا از دوباره نویسی توابع و پراپرتی های تکراری پرهیز کنید. این کار به سرعت نوشتن اپلیکیشن و درک بهتر کد ها کمک میکنه.

فرض کنید میخوایم برای Circle (دایره) و Rectangle (مستطیل) دوتا کلاس تعریف کنیم، این دو آبجکت پراپرتی و توابع مشترکی دارن که هر دو میتونن از یک کلاس مشترک به ارث ببرن.

برای این منظور میتونیم یک کلاس به اسم GeometricShape تعریف کنیم که دارای پراپرتی ها و توابع مشترک بین Circle و Rectangle است، و این دو کلاس پراپرتی و توابع GeometricShape رو به ارث ببرن.

ارث بری دو کلاس
ارث بری دو کلاس Circle و Rectangle از GeometricShape

به کلاسی که ویژگی های مشترک دایره و مستطیل رو داره کلاس پدر سوپر کلاس (superclass) و به کلاس های دایره و مستطیل که ازش ارث میبرن کلاس بچه یا ساب کلاس (subclass)میگیم.

نحوه ی ارث بردن از یک کلاس

برای ارث بردن از سوپر کلاس (superclass) باید سوپر کلاس با کلیدواژه ی open تعریف بشه.

open class Parent{ }

هنگام تعریف subclass با استفاده از دو نقطه بعد از اسم subclass و کانستراکتور اولیه اش (در صورت تعریف آشکار کانستراکتور) اسم superclass رو می نویسیم.

open class Parent{ } class Child: Parent{ }

هنگام ایجاد آبجکت از subclass ابتدا superclass آماده سازی میشه و سپس subclass.

//superclass open class Parent{ init{ println("This is from the parent") } } //subclass class Child: Parent{ init{ println("This is from the Child") } } fun main(){ val child = Child() }

اگه در subclass کانستراکتور اولیه داشته باشیم هنگام ارث بری باید یکی از کانسنراکتور های سوپر کلاس مقداردهی بشن.

open class Parent(s: String){ } class Child(b: Boolean, s: String): Parent(s){ }

استفاده از کلیدواژه ی super برای صدا زدن کانستراکتور های superclass

اگه در subclass کانستراکتور اولیه نداشته باشیم، نمیتونیم پارامتر های کانستراکتور superclass رو هنگام ارث بری مقداردهی کنیم؛ بنابراین با استفاده از کلیدواژه ی super بعد از کانستراکتور های ثانویه subclass کانستراکتور های superclass رو مقداردهی میکنیم.

نکته

در این حالت باید از کلیدواژه ی super بعد از تمام کانستراکتور های ثانویه استفاده بشه.

نکته

اگه کانستراکتور ثانویه در ساب کلاس (subclass) با this رجوع کنه به کانستراکتوری که از super استفاده کرده در این حالت نیاری به استفاده از کلیدواژه ی super نیست.

مثال

در زیر کلیدواژه ی this رجوع میکنه به کانستراکتور های خود کلاس و کلیدواژه ی super رجوع میکنه به کانستراکتور های superclass

open class Parent(s: String){ constructor(): this("Assigned in superclass") } class Child: Parent{ constructor(b: Boolean, n: Int, s: String): super(s0){ } constructor(b: Boolean, n: Int): this(b , n, "Assigned in subclass"){ } constructor(n: Int): super(){ } }

باز نویسی (override) کردن توابع

میتونیم توابع superclass رو در subclass بازنویسی کنیم در اینصورت میگیم تابع رو override کردیم.

برای این کار تابع باید در superclass با کلیدواژه ی open تعریف شده باشه و در subclass از کلیدواژه ی override برای بازنویسی تابع استفاده کنیم.

open class Parent{ open fun message(){ println("This is a message from the Parent") } } class Child{ override fun message(){ println("Overridden message from the Child") } }

استفاده از کلیدواژه ی super برای صدا زدن کد های تابع در superclass

هنگام override کردن تابع (متد) اگه بخوایم کد های superclass رو در subclass داشته باشیم با استفاده از کلیدواژه ی super، نقطه و اسم تابع کد ها رو صدا میزنیم.

مثال

open class Parent{ open fun message(){ println("This is a message from Parent") } } class Child: Parent{ override fun message(){ println("Overridden message from the Child") //صدا زدن کد های تابع در سوپر کلاس super.message() } }

بازنویسی (override) کردن پراپرتی ها

مانند توابع، پراپرتی ها باید در سوپر کلاس با کلیدواژه ی open تعریف بشن؛ سپس در subclass با کلیدواژه ی override میتونیم بازنویسیشون کنیم.

نکته:

هنگام override کردن یک پراپرتی (property) اگه پراپرتی با val تعریف شده بود میتونیم با var بازنویسیش کنیم اما برعکس ممکن نیست.

open class FilledCircle { var radius: Double = 1.0 open val isFilled = true } class Circle: FilledCircle{ override var isFilled = false }

معرفی کلاس Any

کلاس Any در کاتلین مانند کلاس Object در جاواست، این کلاس دارای سه تابع (متد) hashcode، equals و toString است.

هر کلاسی که superclass نداشته باشه به طور پیشفرض از این کلاس ارث بری میکنه بنابراین میشه نتیجه گرفت کلاس Any سوپر کلاس تمام کلاس ها به طور مستقیم یا غیر مستقیم است.

مثال

class Line(var x: Double = 0, var x1: Double = 0, var y: Double = 0, var y0: Double = 0){ val dateCreated = java.util.Date() val length: Double get() = sqrt((x-x0)*(x-x0) + (y-y0)*(y-y0)) override fun toString(){ return "line created at $dateCreated and its length is $length" } override fun equals(other: Any){ if(other !is Line) throw IllegalArgumentException("Parameter is not Line type") return other.length == this.length } } fun main(){ val line0 = Line(3.0, 4.0, 2.0, 6.0) println(line0.toString()) val line = Line(-3.0, -4.0, -2.0, -6.0) println(line.toString()) val eq = line0.equals(line) println("line0 equals to line1? $eq") }

مورد مطالعه (مثال ها)

یک کلاس تعریف میکنیم به اسم Shape که ویژگی های عمومی یک شکل رو به همراه داره سپس با کلاس GeometricShape این ویزگی ها رو به ارث می بریم و به علاوه ویژگی های جدید به GeometricShape اضافه میکنیم و در نهایت با دو کلاس Rectangle و Circle از کلاس GeometricShape ارث بری میکنیم و کلاس های اختصاصی دایره و مستطیل رو به وجود میاریم.

ارث بری
ارث بری از Shape و GeometricShape
open class Shape(var color: String, var isFilled: Boolean = false){ val dateCreated = java.util.Date() override fun toString(): String{ return "Shape created at $dateCreated \ncolor is $color, \nisFilled? $isFilled" } } open class GeometricShape(color: String, isFilled: Boolean): Shape(color, isFilled){ open val area get() = 0 open val perimeter get() = 0 constructor(): this("White", false) override fun toString(): String{ return super.toString() + "\nArea is $area \nperimeter is $perimeter \nX is $x and Y is $y" } override fun equals(other: Any?): Boolean{ if(other !is GeometricShape) throw IllegalArgumentException("GeometricShape argument required") return other.area == this.area } } // تعریف کلاس دایره class Circle: GeometricShape{ var radius = 1 override val area get() = (radius * radius * PI).toInt() override val perimeter get() = (2 * radius * PI).toInt() constructor(radius: Int, color: String, isFilled: Boolean): super(color, isFilled){ this.radius = radius } constructor(): super() constructor(radius: Int): super(){ this.radius = radius } override fun toString(): String{ val fromGeometricShape = super.toString() return fromGeometricShape + "\nThe shape is Circle!" } } // تعریف کلاس مستطیل class Rectangle: GeometricShape{ var width = 1 var height = 1 override val area get() = width * height override val perimeter get() = (width + height) * 2 constructor(width: Int, height: Int, color: String, isFilled: Boolean): super(color, isFilled){ this.width = width this.height = height } constructor(): super() constructor(width: Int, height: Int): super(){ this.width = width this.height = height } fun isSquare(): Boolean = (width == height) override fun toString(): String{ val fromGeometricShape = super.toString() return fromGeometricShape + "\nThe shape is Rectangle!" } } fun main(){ val c = Circle(2) c.stroke = "Blue" val c1 = Circle(radius = 5, x = 1, y = 1, "Yellow", true) val rect = Rectangle(3 , 4) val rect1 = Rectangle(7, 7) println("$c") println("\n$c1") println("\n$rect") println("\n$rect1") println() println("Are c and rect equal? ${c.equals(rect)}") println("Are c1 and c equal? ${c1.equals(c)}" ) println() println("Is rect square? ${rect.isSquare()}") println("Is rect1 square? ${rect1.isSquare()}") }

خلاصه

- از وراثت برای جلوگیری از دوباره نویسی کد های یک کلاس در کلاس دیگه استفاده میشه.

- در وراثت به کلاسی که ازش ارث برده میشه superclass و کلاسی که ارث می بره subclass گفته میشه.

- هنگامی که superclass کانستراکتور اولیه داشته باشه باید پارامتر های یکی از کانستراکتور هاش موقع ارث بری مقداردهی بشن.

- از کلیدواژه ی super برای صدا زدن کانستراکتور های superclass میتونیم استفاده کنیم.

- میتونیم توابع superclass رو در subclass بازنویسی کنیم.

- هنگام بازنویسی یک تابع میتونیم با استفاده از کلیدواژه ی super کد های تابع در superclass رو داشته باشیم.

- میتونیم پراپرتی ها در superclass رو در subclass بازنویسی کنیم.

- کلاس Any به طور پیشفرض superclass کلاسی است که از کلاسی ارث بری نکرده.

- کلاس Any سوپر کلاس مستقیم و غیر مستقیم تمام کلاس هاست.

arrow_drop_up
کپی شد!