Kotlin: شرح الوراثة والواجهات في كوتلن
آخر تحديث: 2026-08-26
نموذج الوراثة في كوتلن أكثر أمانًا من جافا: الفئات final بشكل افتراضي والطرق غير قابلة للتجاوز بشكل افتراضي — تشارلي يجب أن يُعلن open صراحةً للسماح بالوراثة، مع المُترجِم يحرس أمان تعدد الأشكال.
1. ما ستتعلمه
open/abstract: الفئات نهائية بشكل افتراضي، تتطلب فتحًا صريحًا- تطبيقات الواجهة الافتراضية
- توثيق
overrideإلزامي - حل تعارضات الوراثة المتعددة:
super<ParentA> - تشارلي عمليًا: هرمية وراثة BulkOrder / PriorityOrder
2. قصة مهندس معمارية حقيقية
(1) مشكلة: تجاوز عرضي يسبب حوادث إنتاجية
بوب ورّث فئة Order لإنشاء SpecialOrder وتجاوز عرضيًا طريقة calculateTotal — مما تسبب في 500 طلب خاص بمبالغ غير صحيحة. فريق المالية رفع تنبيهًا عاجلاً.
(2) حارس وقت الترجمة في كوتلن
KOTLIN
// جافا: توثيق override اختياري - تجاوز صامت ممكن
class SpecialOrder extends Order {
double calculateTotal() { ... } // يتجاوز بصمت!
}
// كوتلن: override إلزامي - المُترجِم يلتقط الأخطاء
open class Order {
open fun calculateTotal(): Double = ...
}
class SpecialOrder : Order() {
override fun calculateTotal(): Double = ... // يجب إعلان override
}
توثيق
overrideالإلزامي + فئات نهائية افتراضيًا = المُترجِم يحرس أمان الوراثة. بوب لن يتجاوز عرضيًا مرة أخرى.
3. نموذج الوراثة المفتوحة
(1) الفئات نهائية بشكل افتراضي
KOTLIN
// افتراضي: الفئة نهائية (لا يمكن توارثها)
class Order(val id: String) // لا يمكن توسيعها
// open: السماح بالوراثة صراحةً
open class Order(val id: String) {
// الطرق أيضًا نهائية بشكل افتراضي
fun process() { } // لا يمكن تجاوزها
// طريقة مفتوحة: السماح بالتجاوز صراحةً
open fun calculateTotal(): Double = 0.0
}
// abstract: يجب توارثها، لا يمكن استنساخها
abstract class BaseOrder(val id: String) {
// طريقة مجردة: يجب تنفيذها من الفئة الفرعية
abstract fun calculateTotal(): Double
// طريقة ملموسة في فئة مجردة
fun getId(): String = id
}
(2) هرمية مُعدِّلات الوراثة
| المُعدِّل | الفئة | الطريقة | قابل للاستنساخ | قابل للتوارث | قابل للتجاوز |
|---|---|---|---|---|---|
| افتراضي (final) | ✅ | ✅ | ✅ | ❌ | ❌ |
open |
✅ | ✅ | ✅ | ✅ | ✅ |
abstract |
✅ | ✅ | ❌ | ✅ (يجب) | ✅ (يجب) |
(3) مقارنة الوراثة بين جافا وكوتلن
| البعد | جافا | كوتلن |
|---|---|---|
| افتراضي الفئة | قابلة للتوارث | نهائية (غير قابلة للتوارث) |
| افتراضي الطريقة | قابلة للتجاوز | نهائية (غير قابلة للتجاوز) |
| توثيق override | @Override (اختياري) |
override (إلزامي) |
| فتح الوراثة | لا يحتاج إعلان | إعلان open صريح |
| فلسفة التصميم | "مفتوح بشكل افتراضي" | "مغلق بشكل افتراضي" |
4. الواجهات والتطبيقات الافتراضية
(1) تعريف الواجهة
KOTLIN
interface Discountable {
// طريقة مجردة
fun discountRate(): Double
// تطبيق افتراضي
fun applyDiscount(total: Double): Double {
return total * (1.0 - discountRate())
}
}
interface Trackable {
val trackingCode: String? // خاصية مجردة
fun track(): String = trackingCode?.let { "Tracking: $it" } ?: "Not shipped"
}
// تنفيذ واجهات متعددة
class PremiumOrder(
val id: String,
val total: Double,
override val trackingCode: String?
) : Discountable, Trackable {
override fun discountRate() = 0.15 // خصم 15%
}
(2) الواجهة مقابل الفئة المجردة
| البعد | الواجهة | الفئة المجردة |
|---|---|---|
| المنشئ | لا يوجد | يوجد |
| الحالة | لا توجد (خصائص مجردة فقط) | توجد (خصائص ملموسة) |
| الوراثة المتعددة | مدعومة (تنفيذ متعدد) | غير مدعومة (وراثة مفردة) |
| التطبيق الافتراضي | مدعوم في كوتلن | مدعوم |
| حالة الاستخدام | عقد السلوك | حالة مشتركة + سلوك |
5. توثيق override الإلزامي
(1) الاستخدام الأساسي
KOTLIN
open class Order(val id: String, var status: String) {
open fun summary(): String = "Order $id: $status"
}
class BulkOrder(id: String, status: String, val minQuantity: Int) : Order(id, status) {
// يجب استخدام كلمة override
override fun summary(): String = "Bulk $id: $status (min: $minQuantity)"
}
(2) منع التجاوز الإضافي
KOTLIN
open class Order {
open fun process() { }
}
class SpecialOrder : Order() {
// منع التجاوز الإضافي في الفئات الفرعية
final override fun process() { }
}
6. حل تعارضات الوراثة المتعددة
(1) مشكلة الوراثة المعينية
KOTLIN
interface A {
fun hello() = "A"
}
interface B {
fun hello() = "B"
}
// معين: C يرث hello() من كل من A وB
class C : A, B {
// يجب تجاوز لحل التعارض
override fun hello(): String {
// اختيار تطبيق الوالد صراحةً
return super<A>.hello() // اختيار تطبيق A
}
}
(2) استراتيجيات حل التعارض
KOTLIN
class D : A, B {
override fun hello(): String {
// استراتيجية 1: اختيار والد واحد
return super<B>.hello()
// استراتيجية 2: دمج كليهما
// return "${super<A>.hello()} + ${super<B>.hello()}"
// استراتيجية 3: تطبيق مخصص
// return "D"
}
}
(3) مقارنة حل التعارضات
| الاستراتيجية | البنية | حالة الاستخدام |
|---|---|---|
| اختيار الوالد A | super<A>.hello() |
تطبيق A أكثر ملاءمة |
| اختيار الوالد B | super<B>.hello() |
تطبيق B أكثر ملاءمة |
| دمج كليهما | super<A> + super<B> |
الحاجة لكلا السلوكين |
| تطبيق مخصص | كود مخصص | لا منهما مناسب |
7. مخطط هرمية الوراثة
classDiagram
class Order {
+val id: String
+var status: String
+open fun summary(): String
+open fun calculateTotal(): Double
}
class BulkOrder {
+val minQuantity: Int
+override fun calculateTotal(): Double
}
class PriorityOrder {
+val priority: String
+override fun summary(): String
}
class Discountable {
<<interface>>
+fun discountRate(): Double
+fun applyDiscount(total: Double): Double
}
class Trackable {
<<interface>>
+val trackingCode: String?
+fun track(): String
}
Order <|-- BulkOrder
Order <|-- PriorityOrder
BulkOrder ..|> Discountable
PriorityOrder ..|> Trackable
PriorityOrder ..|> Discountable
8. مثال كامل: هرمية وراثة OrderProcessor
KOTLIN
// ============================================
// OrderProcessor - هرمية الوراثة
// الميزة: Order / BulkOrder / PriorityOrder
// ============================================
interface Discountable {
fun discountRate(): Double
fun applyDiscount(total: Double): Double = total * (1.0 - discountRate())
}
interface Trackable {
val trackingCode: String?
fun track(): String = trackingCode?.let { "Tracking: $it" } ?: "Not shipped yet"
}
open class Order(val id: String, var status: String, val total: Double) {
init {
require(total >= 0) { "Total must be non-negative" }
}
open fun summary(): String = "Order $id | \$$total USD | $status"
open fun calculateFinal(): Double = total
}
class BulkOrder(
id: String,
status: String,
total: Double,
val minQuantity: Int
) : Order(id, status, total), Discountable {
override fun discountRate() = 0.10 // خصم جملة 10%
override fun calculateFinal(): Double = applyDiscount(total)
override fun summary(): String =
"${super.summary()} | Bulk (min: $minQuantity) | Final: \$${calculateFinal()} USD"
}
class PriorityOrder(
id: String,
status: String,
total: Double,
val priority: String,
override val trackingCode: String?
) : Order(id, status, total), Discountable, Trackable {
override fun discountRate() = when (priority) {
"VIP" -> 0.20
"GOLD" -> 0.15
else -> 0.05
}
override fun calculateFinal(): Double = applyDiscount(total)
override fun summary(): String =
"${super.summary()} | Priority: $priority | ${track()} | Final: \$${calculateFinal()} USD"
}
fun main() {
val orders = listOf(
Order("ORD-001", "CONFIRMED", 299.99),
BulkOrder("ORD-002", "CONFIRMED", 5_000.00, 100),
PriorityOrder("ORD-003", "SHIPPED", 15_000.00, "VIP", "TRK-XYZ789"),
PriorityOrder("ORD-004", "PENDING", 2_500.00, "GOLD", null)
)
println("=== Order Summary ===")
orders.forEach { println(it.summary()) }
val totalSavings = orders
.filterIsInstance<Discountable>()
.sumOf { it.applyDiscount((it as Order).total) - (it as Order).calculateFinal() }
println("\nTotal savings from discounts: \$$totalSavings USD")
}
المخرجات:
TEXT
📖 للعرض فقط
=== Order Summary ===
Order ORD-001 | $299.99 USD | CONFIRMED
Order ORD-002 | $5000.0 USD | CONFIRMED | Bulk (min: 100) | Final: $4500.0 USD
Order ORD-003 | $15000.0 USD | SHIPPED | Priority: VIP | Tracking: TRK-XYZ789 | Final: $12000.0 USD
Order ORD-004 | $2500.0 USD | PENDING | Priority: GOLD | Not shipped yet | Final: $2125.0 USD
Total savings from discounts: $3475.0 USD
9. أمثلة عملية سريعة
▶ مثال: وراثة بسيطة
KOTLIN
// فئة أساسية - يجب أن تكون open لتكون قابلة للوراثة
open class Animal(val name: String, val sound: String) {
open fun makeSound(): String = "$name says $sound!"
fun describe(): String = "This is $name, a $sound maker"
}
// فئة فرعية
class Dog(name: String) : Animal(name, "Woof")
class Cat(name: String) : Animal(name, "Meow")
val dog = Dog("Buddy")
val cat = Cat("Whiskers")
println(dog.makeSound())
println(cat.makeSound())
println(dog.describe())
**المخرجات:
TEXT
📖 للعرض فقط
Buddy says Woof!
Whiskers says Meow!
This is Buddy, a Woof maker
▶ مثال: override و super
KOTLIN
open class Shape(val name: String) {
open fun area(): Double = 0.0
open fun describe(): String = "$name: area=${area()}"
}
class Circle(name: String, val radius: Double) : Shape(name) {
override fun area(): Double = Math.PI * radius * radius
override fun describe(): String = "${super.describe()} (radius=$radius)"
}
class Rectangle(name: String, val width: Double, val height: Double) : Shape(name) {
override fun area(): Double = width * height
}
val circle = Circle("C1", 5.0)
val rect = Rectangle("R1", 4.0, 6.0)
println(circle.describe())
println("${rect.name}: area=${rect.area()}")
**المخرجات:
TEXT
📖 للعرض فقط
C1: area=78.53981633974483 (radius=5.0)
R1: area=24.0
▶ مثال: فئة مجردة (Abstract)
KOTLIN
abstract class Payment(val amount: Double) {
abstract fun process(): Boolean
abstract fun describe(): String
fun receipt(): String = "Receipt: ${describe()} - \$$amount USD"
}
class CreditPayment(amount: Double, val cardNumber: String) : Payment(amount) {
override fun process(): Boolean {
println("Processing credit card payment...")
return true
}
override fun describe(): String = "Credit card ending ${cardNumber.takeLast(4)}"
}
class BankTransfer(amount: Double, val account: String) : Payment(amount) {
override fun process(): Boolean {
println("Processing bank transfer...")
return true
}
override fun describe(): String = "Bank transfer to $account"
}
val cc = CreditPayment(299.99, "1234567890123456")
val bt = BankTransfer(1500.00, "ACC-987654")
println(cc.receipt())
println("Success: ${cc.process()}")
println()
println(bt.receipt())
println("Success: ${bt.process()}")
**المخرجات:
TEXT
📖 للعرض فقط
Receipt: Credit card ending 3456 - $299.99 USD
Processing credit card payment...
Success: true
Receipt: Bank transfer to ACC-987654 - $1500.0 USD
Processing bank transfer...
Success: true
▶ مثال: واجهات متعددة
KOTLIN
interface Printable {
fun print(): String
}
interface Loggable {
fun log(): String
}
interface Serializable {
fun serialize(): String
}
// فئة تنفذ عدة واجهات
class User(val name: String, val email: String) : Printable, Loggable, Serializable {
override fun print(): String = "User: $name ($email)"
override fun log(): String = "[LOG] User $name accessed"
override fun serialize(): String = """{"name":"$name","email":"$email"}"""
}
val user = User("Alice", "alice@example.com")
println(user.print())
println(user.log())
println(user.serialize())
// واجهة مع تطبيق افتراضي
interface Greetable {
fun greet(): String = "Hello!" // تطبيق افتراضي
}
class FriendlyUser(name: String) : Greetable {
// يستخدم التطبيق الافتراضي
override fun greet(): String = "Hi, I'm $name!" // override اختياري
}
**المخرجات:
TEXT
📖 للعرض فقط
User: Alice (alice@example.com)
[LOG] User Alice accessed
{"name":"Alice","email":"alice@example.com"}
▶ مثال: abstract class vs interface
KOTLIN
// abstract class: يمكن أن يكون له حالة (خصائص مخزنة)
abstract class Vehicle(val maxSpeed: Double) {
var currentSpeed: Double = 0.0
abstract fun start()
fun stop() {
currentSpeed = 0.0
println("Vehicle stopped")
}
fun status() = "Speed: $currentSpeed / $maxSpeed km/h"
}
// interface: فقط عقود (منذ Kotlin 1.8 يمكن أن يكون لها تطبيقات افتراضية)
interface Electric {
val batteryLevel: Int // خاصية مجردة
fun charge() {
println("Charging...")
}
}
class Tesla(val model: String) : Vehicle(250.0), Electric {
override var batteryLevel: Int = 100
override fun start() {
currentSpeed = 30.0
println("$model started silently")
}
override fun charge() {
batteryLevel = 100
println("$model charged to 100%")
}
}
val myCar = Tesla("Model 3")
myCar.start()
println(myCar.status())
println("Battery: ${myCar.batteryLevel}%")
myCar.stop()
myCar.charge()
**المخرجات:
TEXT
📖 للعرض فقط
Model 3 started silently
Speed: 30.0 / 250.0 km/h
Battery: 100%
Vehicle stopped
Model 3 charged to 100%
▶ مثال: polymorphism و casting
KOTLIN
open class Animal2 {
open fun sound() = "Generic sound"
}
class Dog2 : Animal2() {
override fun sound() = "Woof"
fun fetch() = "Fetching!"
}
class Cat2 : Animal2() {
override fun sound() = "Meow"
}
// دالة تقبل Animal2 وتستخدم السلوك الديناميكي
fun interact(animal: Animal2) {
println("${animal.sound()}")
// فحص النوع وتحويل آمن
if (animal is Dog2) {
println("Dog detected: ${animal.fetch()}")
} else if (animal is Cat2) {
println("Cat detected")
}
}
val animals: List<Animal2> = listOf(Dog2(), Cat2(), Dog2())
animals.forEach { interact(it) }
// smart cast داخل if
val animal: Animal2 = Dog2()
if (animal is Dog2) {
// animal أصبح Dog2 تلقائيًا
println("Fetch: ${animal.fetch()}")
}
**المخرجات:
TEXT
📖 للعرض فقط
Woof
Dog detected: Fetching!
Meow
Cat detected
Woof
Dog detected: Fetching!
Fetch: Fetching!
▶ مثال: نوعي الوراثة (Bounded)
KOTLIN
// الفئة العليا: قابل للمقارنة
abstract class ComparableEntity<T : ComparableEntity<T>>(val id: String) : Comparable<T> {
override fun compareTo(other: T): Int = this.id.compareTo(other.id)
}
class UserEntity(id: String, val name: String) : ComparableEntity<UserEntity>(id)
class OrderEntity(id: String, val total: Double) : ComparableEntity<OrderEntity>(id)
val users = listOf(
UserEntity("U003", "Charlie"),
UserEntity("U001", "Alice"),
UserEntity("U002", "Bob")
)
val orders = listOf(
OrderEntity("O002", 1500.0),
OrderEntity("O001", 299.99)
)
// sorted يستخدم compareTo
val sortedUsers = users.sorted()
val sortedOrders = orders.sorted()
println("Sorted users:")
sortedUsers.forEach { println(" ${it.id}: ${it.name}") }
println("\nSorted orders:")
sortedOrders.forEach { println(" ${it.id}: \$${it.total} USD") }
**المخرجات:
TEXT
📖 للعرض فقط
Sorted users:
U001: Alice
U002: Bob
U003: Charlie
Sorted orders:
O001: $299.99 USD
O002: $1500.0 USD
❓ أسئلة شائعة
س لماذا فئات كوتلن نهائية بشكل افتراضي؟
ج Effective Java يوصي بـ "صمم ووثّق للوراثة، أو احظرها." كوتلن يتبنى هذه النصيحة، افتراضيًا يحظر الوراثة، ويستخدم
open للفتح الصريح.س هل يمكن أن تمتلك الواجهات منشآت؟
ج لا. الواجهات لا تمتلك منشآت ولا يمكنها الاحتفاظ بحالة. استخدم الفئات المجردة للحالة المشتركة.
س هل يمكن لفئة حل التعارضات عند تنفيذ واجهات متعددة بطرق افتراضية؟
ج نعم. إذا كانت واجهتان لديهما طرق افتراضية بنفس الاسم والتوقيع، يجب على الفئة الفرعية تجاوز الطريقة واستخدام
super<InterfaceName> لاختيار تطبيق.س هل يمكن حذف
override؟ج لا. كوتلن يُلزم كلمة
override — حذفها يسبب خطأ ترجمة. هذه آلية الأمان الرئيسية لمنع التجاوزات العرضية.س هل تحتاج طرق الفئة المجردة لـ
open؟ج لا. الطرق المجردة في الفئات المجردة قابلة للتجاوز ضمنيًا (يجب تجاوزها). الطرق المفتوحة غير المجردة لا تزال تحتاج المُعدِّل
open.س هل يدعم كوتلن استدعاء
super مثل جافا؟ج نعم. منشآت الفئة الفرعية تستخدم
super(...) لاستدعاء منشئ الوالد، الطرق تستخدم super.foo() لاستدعاء تطبيق الوالد، والواجهات تستخدم super<Interface>.foo().📖 ملخص
- فئات كوتلن نهائية بشكل افتراضي؛ يجب أن تكون
openللوراثة — مغلق افتراضيًا، يُفتح صراحةً - الفئات
abstractلا يمكن استنساخها؛ الطرق المجردة يجب تنفيذها من الفئات الفرعية - الواجهات تدعم التطبيقات الافتراضية والخصائص المجردة؛ التنفيذ المتعدد مسموح
- توثيق
overrideالإلزامي يمنع التجاوزات العرضية — المُترجِم يحرس أمان تعدد الأشكال - تعارضات الوراثة المتعددة تُحل بالاختيار الصريح
super<Interface> - استخدم الواجهات لعقود السلوك (بدون حالة)، الفئات المجردة للحالة المشتركة + السلوك
📝 تمارين
- أساسي (⭐): عرّف
OrderوBulkOrderيرث منها، مع تجاوز طريقةsummary. تلميح:open class Order،class BulkOrder : Order() - متوسط (⭐⭐): أنشئ واجهات
DiscountableوTrackable، اجعلPriorityOrderتنفذ كليهما، مع حل تعارض أسماء. تلميح:super<Discountable> - تحدي (⭐⭐⭐): صمم هرمية وراثة طلبات كاملة:
BaseOrder→BulkOrder/PriorityOrder، كل منها ينفذ استراتيجيات خصم وشحن مختلفة. تلميح: اجمع بين الواجهات والفئات المجردة