به کلاس، تابع و پراپرتی های معمولی concrete میگیم اما نوعی از کلاس در کاتلین وجود داره که بهش abstract میگیم و میتونیم داخلش علاوه بر توابع و پراپرتی های concrete، توابع و پراپرتی های abstract نیز تعریف کنیم.
میتونیم یک تابع (متد) رو abstract تعریف کنیم و هنگام ارث بری و بازنویسی تابعو کامل پیادهسازی کنیم برای این کار تابع باید در کلاس abstract یا در interface تعریف بشه و هنگام ارث بری بازنویسی بشه، در این مطلب کلاس های ابسترکت رو بررسی میکنیم و در مطلب بعدی به اینترفیس ها می پردازیم.
برای تعریف کلاس ابسترکت از کلیدواژه ی abstract قبل از کلیدواژه ی class استفاده میکنیم و برای تعریف پراپرتی و توابع ابسترکت هم مثل کلاس ابسترکت ابتدا کلیدواژه ی abstract رو مینویسیم.
با اینکه میتونیم در کلاس ابسترکت، کانستراکتور تعریف کنیم اما نمیتونیم مستقیم از این کلاس ها نمونه (شی) سازی کنیم و باید از ساب کلاسی که concrete است نمونه سازی کنیم.
فرم کلی کلاس های ابسترکت:
abstract class MyAbstractClass{
//abstract property
abstract val t0: T
//concrete property
val t1: T = ...
...
//abstract function
abstract fun myAbstractFunction(t: T)
//concrete function
fun myFunction(t: T){
//Do Something Here...
}
...
}
همینطور که در بالا مشخصه در کلاس های ابسترکت، علاوه بر توابع و متغیر های concrete توابع و متغیر های abstract نیز میتونیم تعریف کنیم.
اگه ساب کلاس concrete بود توابع و پراپرتی های abstract تعریف شده در سوپر کلاس باید به صورت concrete (کامل) در subclass بازنویسی بشن
مثال
فرض کنید کلاس Foo سوپر کلاس و Bar ساب کلاس Foo است.
abstract class Foo{
abstract val prop0: String
abstract fun abstractSayHello()
fun concreteSayHello(){
println("Hello from concrete function!")
}
}
class Bar: Foo{
override val prop0 = "Hello from abstract property!"
override fun abstractSayHello(){
println("Hello from Abstract function!")
}
}
fun main(){
val bar = Bar()
println(bar.prop0)
bar.abstractSayHello()
bar.concreteSayHello()
}
هنگامی که در یک کلاس abstract متغیر یا تابع abstract تعریف نکنیم معمولا به عنوان کلاس Base در نظرش میگیریم. کلاس Base پایه و سوپر کلاس تمام subclass های یک بخشی از پروژه است.
abstract class MyBase{
//without abstract properties and (or) functions
...
}
مورد مطالعه (مثال ها)
در مطلب وراثت کلاس Circle و Rectangle از کلاس GeometricShape ارث بری کردن و کلاس GeometricShape از کلاس Shape.
نمودار رابطه ی بین این چهار کلاس نیز در زیر ترسیم شده:
ارث بری از Shape و GeometricShape
حالا میخوایم دو کلاس Shape و GeometricShape رو به صورت abstract تعربف کنیم:
abstract 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"
}
}
abstract class GeometricShape(color: String, isFilled: Boolean): Shape(color, isFilled){
abstract val area: Int
abstract val perimeter: Int
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()}")
}
خلاصه
به توابع و متغیر های کامل concrete میگیم.
- متغیر و پراپرتی های ابسترکت روفقط داخل کلاس ابسترکت یا داهل اینترفیس مبتونیم تعریف کنیم.
در کلاس ابسترکت علاوه بر پراپرتی و توابع ابسترکت میتونیم پراپرتی و توابع کامل (concrete) نیز تعریف کنیم.
با اینکه میتونیم برای کلاس ابسترکت، کانستراکتور تعریف کنیم اما نمیتونیم از اون کلاس نمونه ایجاد کنیم.
معمولا کلاس ابسترکتی که پراپرتی و تابع ابسترکت نداشته باشه رو به عنوان کلاس Base تعریف میکنن.