Kotlin: شرح الفئات والكائنات في كوتلن

آخر تحديث: 2026-08-26

فلسفة تصميم الفئات في كوتلن: المنشئ الأساسي + الخصائص في سطر واحد، كتل init تحل محل جسم المنشئ في جافا — تشارلي يُعرّف فئة Order في 3 أسطر أقوى من 30 سطر مكافئة في جافا.

1. ما ستتعلمه


2. قصة مهندس معمارية حقيقية

(1) مشكلة: جحيم المنشآت في جافا

في جافا، فئة Order الخاصة بتشارلي احتاجت 3 منشآت (بدون وسائط، الحقول المطلوبة، جميع الحقول)، بالإضافة إلى getters/setters/منطق التحقق — 80+ سطر لفئة مجال واحدة.

(2) حل المنشئ الأساسي في كوتلن

KOTLIN
// جافا: 80+ سطر لفئة مجال واحدة
// كوتلن: 3 أسطر مع التحقق
class Order(val id: String, var status: String, val total: BigDecimal) {
    init { require(total >= BigDecimal.ZERO) { "Total must be non-negative" } }
}

المنشئ الأساسي + الخصائص + كتلة init مجتمعة — 80 سطر جافا → 3 أسطر كوتلن.


3. المنشئ الأساسي والخصائص

(1) المنشئ الأساسي في سطر واحد

KOTLIN
// المنشئ الأساسي مع الخصائص في سطر واحد
class Order(val id: String, var status: String, val total: Double)

// المكافئ في جافا سيكون 20+ سطر

(2) معاملات المنشئ مقابل الخصائص

KOTLIN
// val/var في المنشئ = خصائص
class Order(val id: String,    // خاصية للقراءة فقط
            var status: String, // خاصية قابلة للتغيير
            total: Double)      // مجرد معامل منشئ، ليست خاصية

// الاستخدام
val order = Order("ORD-001", "PENDING", 299.99)
println(order.id)      // موافق - خاصية val
order.status = "PAID"  // موافق - خاصية var
// order.total         // خطأ - ليست خاصية

(3) المنشئ الأساسي مقابل المنشئ الثانوي

KOTLIN
class Order(val id: String, var status: String, val total: Double) {
    // المنشئ الثانوي يجب أن يفوّض للأساسي
    constructor(id: String) : this(id, "PENDING", 0.0)

    // منشئ ثانوي آخر
    constructor(id: String, total: Double) : this(id, "PENDING", total)
}

val order1 = Order("ORD-001")               // ثانوي
val order2 = Order("ORD-002", 299.99)        // ثانوي
val order3 = Order("ORD-003", "CONFIRMED", 1_500.00)  // أساسي

(4) مقارنة المنشآت

البعد جافا كوتلن
تعريف الخصائص + المنشئ منفصلان المنشئ الأساسي في سطر واحد
منشآت متعددة تعريفات مستقلة الثانوي يجب أن يفوّض للأساسي

4. كتلة init

كتلة init تعمل فورًا بعد المنشئ الأساسي — تُستخدم للتحقق ومنطق التهيئة.

(1) الاستخدام الأساسي

KOTLIN
class Order(val id: String, var status: String, val total: Double) {
    init {
        require(total >= 0.0) { "Total must be non-negative, got $total" }
        require(id.startsWith("ORD-")) { "Order ID must start with ORD-" }
    }

    // كتل init متعددة تنفذ بالترتيب
    init {
        println("Order $id created with total \$$total USD")
    }
}

(2) ترتيب تنفيذ كتلة init

KOTLIN
class Example {
    val a = println("1: property initialization")

    init {
        println("2: first init block")
    }

    val b = println("3: property initialization")

    init {
        println("4: second init block")
    }
}
// المخرجات: 1, 2, 3, 4 (ترتيب الإعلان)

5. مُعدِّلات الرؤية

(1) أربع مستويات الرؤية

KOTLIN
class OrderProcessor {
    // public (افتراضي): مرئي في كل مكان
    fun process(order: Order) { ... }

    // private: مرئي داخل هذه الفئة فقط
    private fun validate(order: Order) { ... }

    // protected: مرئي في هذه الفئة والفئات الفرعية
    protected fun calculateTax(order: Order) { ... }

    // internal: مرئي داخل نفس الوحدة
    internal fun report() { ... }
}

(2) جدول مقارنة الرؤية

المُعدِّل داخل الفئة فئة فرعية نفس الوحدة عام
public
internal
protected
private

(3) اختلافات الرؤية بين جافا وكوتلن

البعد جافا كوتلن
الرؤية الافتراضية package-private public
رؤية الوحدة لا توجد internal
رؤية الحزمة package-private لا توجد (استخدم internal بدلاً)
إعلانات المستوى الأعلى public فقط public / internal / private

6. التهيئة المؤجلة

(1) lateinit var

KOTLIN
class OrderService {
    // lateinit: وعد بالتهيئة قبل الاستخدام
    lateinit var repository: OrderRepository

    fun init(repo: OrderRepository) {
        repository = repo
    }

    fun process(order: Order) {
        // الوصول قبل التهيئة يرمي UninitializedPropertyAccessException
        repository.save(order)
    }
}

(2) by lazy

KOTLIN
class OrderProcessor {
    // lazy: آمن للخيوط، يُهيَّأ عند أول وصول
    val cache: OrderCache by lazy {
        println("Initializing cache...")
        OrderCache(maxSize = 10_000)
    }

    // lazy مع وضع قفل مخصص
    val heavyResource by lazy(LazyThreadSafetyMode.PUBLICATION) {
        loadHeavyResource()
    }
}

(3) مقارنة lateinit مقابل by lazy

البعد lateinit by lazy
النوع var (قابل للتغيير) val (للقراءة فقط)
توقيت التهيئة تعيين يدوي عند أول وصول
أمان الخيوط لا نعم (افتراضي)
القابلية للفراغ مُعلن غير فارغ مُعلن غير فارغ
الوصول قبل التهيئة استثناء وقت التشغيل لن يحدث
حالة الاستخدام حقن إطار DI خصائص حسابية مكلفة

7. مخطط علاقات الفئات

100%
classDiagram
    class Order {
        +val id: String
        +var status: String
        +val total: Double
        +val items: List~OrderItem~
        +fun addItem(item: OrderItem)
    }
    class OrderItem {
        +val sku: String
        +val quantity: Int
        +val unitPrice: Double
        +fun subtotal: Double
    }
    class Customer {
        +val id: String
        +val name: String
        +val email: String?
        +val address: Address?
    }
    class Address {
        +val street: String
        +val city: String
        +val country: String
    }
    Order --> OrderItem : contains
    Order --> Customer : belongs to
    Customer --> Address : has

8. مثال كامل: نموذج مجال OrderProcessor

KOTLIN
// ============================================
// OrderProcessor - نموذج المجال
// الميزة: Order، OrderItem، Customer مع تحقق init
// ============================================

import java.math.BigDecimal
import java.math.RoundingMode

class Address(val street: String, val city: String, val country: String) {
    override fun toString(): String = "$street, $city, $country"
}

class Customer(val id: String, val name: String, val email: String?) {
    var address: Address? = null

    fun getDisplayEmail(): String = email ?: "no-email"

    override fun toString(): String = "Customer($id, $name, ${getDisplayEmail()})"
}

class OrderItem(val sku: String, val quantity: Int, val unitPrice: BigDecimal) {
    init {
        require(quantity > 0) { "Quantity must be positive, got $quantity" }
        require(unitPrice >= BigDecimal.ZERO) { "Price must be non-negative" }
    }

    val subtotal: BigDecimal
        get() = unitPrice.multiply(BigDecimal(quantity)).setScale(2, RoundingMode.HALF_UP)
}

class Order(
    val id: String,
    var status: String,
    private val _items: MutableList<OrderItem> = mutableListOf()
) {
    init {
        require(id.startsWith("ORD-")) { "Order ID must start with ORD-" }
    }

    val items: List<OrderItem> get() = _items.toList()

    val total: BigDecimal
        get() = _items.fold(BigDecimal.ZERO) { acc, item -> acc.add(item.subtotal) }

    fun addItem(item: OrderItem) {
        _items.add(item)
    }

    val itemCount: Int get() = _items.size

    // ضريبة محسوبة مؤجلة
    val tax by lazy {
        total.multiply(BigDecimal("0.08")).setScale(2, RoundingMode.HALF_UP)
    }

    override fun toString(): String = "Order($id, $status, ${itemCount} items, \$$total USD)"
}

fun main() {
    val customer = Customer("CUST-001", "Alice", "alice@example.com").also {
        it.address = Address("123 Main St", "New York", "US")
    }
    println(customer)
    println("Address: ${customer.address}")

    val order = Order("ORD-001", "PENDING")
    order.addItem(OrderItem("SKU-WIDGET", 3, BigDecimal("9.99")))
    order.addItem(OrderItem("SKU-GADGET", 1, BigDecimal("149.99")))
    order.addItem(OrderItem("SKU-DOOHICKEY", 5, BigDecimal("4.50")))

    println(order)
    println("Subtotal: \$$${order.total} USD")
    println("Tax: \$$${order.tax} USD")
    println("Grand Total: \$$${order.total.add(order.tax)} USD")

    order.status = "CONFIRMED"
    println("Status updated: ${order.status}")
}

المخرجات:

TEXT 📖 للعرض فقط
Customer(CUST-001, Alice, alice@example.com)
Address: 123 Main St, New York, US
Order(ORD-001, PENDING, 3 items, $199.42 USD)
Subtotal: $$199.42 USD
Tax: $$15.95 USD
Grand Total: $$215.37 USD
Status updated: CONFIRMED

9. أمثلة عملية سريعة

▶ مثال: فئة بسيطة مع خصائص

KOTLIN
// تعريف فئة مع خصائص للقراءة فقط (val) وكتابة (var)
class Person(val name: String, var age: Int) {
    // كتلة init للتحقق من الصحة
    init {
        require(age >= 0) { "Age cannot be negative: $age" }
    }

    // خاصية محسوبة
    val isAdult: Boolean
        get() = age >= 18

    // طريقة
    fun greet(): String = "Hello, my name is $name and I am $age years old"

    // طريقة مع قيمة افتراضية
    fun haveBirthday(years: Int = 1) {
        age += years
        println("$name is now $age years old")
    }
}

val alice = Person("Alice", 30)
println(alice.greet())
println("Is adult: ${alice.isAdult}")
alice.haveBirthday()
println(alice.greet())

// try { Person("Bob", -5) } catch (e: Exception) { println("Error: ${e.message}") }

**المخرجات:

TEXT 📖 للعرض فقط
Hello, my name is Alice and I am 30 years old
Is adult: true
Alice is now 31 years old
Hello, my name is Alice and I am 31 years old

▶ مثال: المُنشآت (Constructors)

KOTLIN
class Order(val id: String, val total: Double) {
    var status: String = "PENDING"
    private var lastModified: Long = System.currentTimeMillis()

    // كتلة init ثانوية
    init {
        require(total >= 0) { "Total cannot be negative" }
        println("Order $id created with total $total")
    }

    // منشأ ثانوي: يفوض للمنشئ الأساسي
    constructor(id: String) : this(id, 0.0) {
        println("Created empty order $id")
    }

    // منشأ مع قيم افتراضية
    constructor() : this("AUTO-${System.currentTimeMillis()}")

    fun confirm() {
        status = "CONFIRMED"
        lastModified = System.currentTimeMillis()
    }

    fun info() = "Order $id: $status (\$$total USD)"
}

val o1 = Order("ORD-001", 299.99)
println(o1.info())
o1.confirm()
println(o1.info())

val o2 = Order("ORD-002")
println(o2.info())

val o3 = Order()
println(o3.info())

**المخرجات:

TEXT 📖 للعرض فقط
Order ORD-001 created with total 299.99
Order ORD-001: PENDING ($299.99 USD)
Order ORD-001: CONFIRMED ($299.99 USD)
Order ORD-002 created with total 0.0
Created empty order ORD-002
Order ORD-002: PENDING ($0.0 USD)
Order AUTO-1700000000000 created with total 0.0
Order AUTO-1700000000000: PENDING ($0.0 USD)

▶ مثال: الوراثة

KOTLIN
// فئة أساسية مفتوحة للوراثة
open class Employee(val name: String, val baseSalary: Double) {
    open fun calculateBonus(): Double = baseSalary * 0.1

    fun describe(): String = "$name earns \$${baseSalary + calculateBonus()} USD"
}

class Manager(name: String, baseSalary: Double, val teamSize: Int) : Employee(name, baseSalary) {
    override fun calculateBonus(): Double {
        return baseSalary * 0.1 + teamSize * 1000
    }
}

class Developer(name: String, baseSalary: Double, val projects: Int) : Employee(name, baseSalary) {
    override fun calculateBonus(): Double {
        return baseSalary * 0.1 + projects * 500
    }
}

val alice = Employee("Alice", 50000.0)
val bob = Manager("Bob", 70000.0, 5)
val charlie = Developer("Charlie", 60000.0, 3)

println(alice.describe())
println(bob.describe())
println(charlie.describe())

**المخرجات:

TEXT 📖 للعرض فقط
Alice earns $55000.0 USD
Bob earns $72000.0 USD
Charlie earns $64500.0 USD

▶ مثال: الواجهات (Interfaces)

KOTLIN
// واجهة تحدد عقد
interface PaymentProcessor {
    fun processPayment(amount: Double): Boolean
    fun refund(transactionId: String): Boolean
}

// فئة تنفذ الواجهة
class CreditCardProcessor(private val apiKey: String) : PaymentProcessor {
    override fun processPayment(amount: Double): Boolean {
        println("Processing credit card payment of \$$amount USD")
        return true
    }

    override fun refund(transactionId: String): Boolean {
        println("Refunding transaction $transactionId")
        return true
    }
}

class PayPalProcessor(private val clientId: String) : PaymentProcessor {
    override fun processPayment(amount: Double): Boolean {
        println("Processing PayPal payment of \$$amount USD")
        return true
    }

    override fun refund(transactionId: String): Boolean {
        println("PayPal refund for $transactionId")
        return true
    }
}

// استخدام الواجهة
fun checkout(processor: PaymentProcessor, amount: Double): Boolean {
    return processor.processPayment(amount)
}

val cc = CreditCardProcessor("api-key-123")
val pp = PayPalProcessor("client-id-456")

checkout(cc, 299.99)
checkout(pp, 1500.00)

**المخرجات:

TEXT 📖 للعرض فقط
Processing credit card payment of $299.99 USD
Processing PayPal payment of $1500.0 USD

▶ مثال: فئات البيانات (Data Classes)

KOTLIN
// data class - توليد تلقائي لـ equals/hashCode/toString/copy
data class Product(val id: String, val name: String, val price: Double)

// إنشاء ونسخ
val p1 = Product("P001", "Laptop", 1299.99)
val p2 = p1.copy(price = 1199.99)  // نسخ مع تغيير حقل واحد

println("p1: $p1")
println("p2: $p2")
println("Equal: ${p1 == p2}")  // false (price مختلف)

// equals/hashCode
val p3 = Product("P001", "Laptop", 1299.99)
println("p1 == p3: ${p1 == p3}")  // true
println("Hash p1 == Hash p3: ${p1.hashCode() == p3.hashCode()}")

// النسخ التدميري
val (id, name, price) = p1
println("Destructured: id=$id, name=$name, price=\$$price")

// قائمة بـ data class
val products = listOf(
    Product("P001", "Laptop", 1299.99),
    Product("P002", "Mouse", 29.99),
    Product("P003", "Keyboard", 79.99)
)
val total = products.sumOf { it.price }
println("Total: \$$total USD")

**المخرجات:

TEXT 📖 للعرض فقط
p1: Product(id=P001, name=Laptop, price=1299.99)
p2: Product(id=P001, name=Laptop, price=1199.99)
Equal: false
p1 == p3: true
Hash p1 == Hash p3: true
Destructured: id=P001, name=Laptop, price=$1299.99
Total: $1409.97 USD

▶ مثال: Companion Objects و Static-like

KOTLIN
class Database private constructor(private val url: String) {
    // companion object مثل static في جافا
    companion object {
        private val instances = mutableMapOf<String, Database>()

        fun connect(url: String): Database {
            return instances.getOrPut(url) { Database(url) }
        }

        const val MAX_CONNECTIONS = 100
    }

    fun query(sql: String): String = "[$url] Executing: $sql"
}

// استخدام
val db1 = Database.connect("localhost:5432")
val db2 = Database.connect("localhost:5432")  // نفس المثيل (Singleton)
println("Same instance: ${db1 === db2}")
println(db1.query("SELECT * FROM users"))
println("Max connections: ${Database.MAX_CONNECTIONS}")

**المخرجات:

TEXT 📖 للعرض فقط
Same instance: true
[localhost:5432] Executing: SELECT * FROM users
Max connections: 100

▶ مثال: Getters و Setters المخصصة

KOTLIN
class Temperature {
    var celsius: Double = 0.0
        set(value) {
            // التحقق من الصحة عند التعيين
            require(value >= -273.15) { "Temperature cannot be below absolute zero" }
            field = value
        }

    // خاصية محسوبة من أخرى
    val fahrenheit: Double
        get() = celsius * 9.0 / 5.0 + 32.0

    val kelvin: Double
        get() = celsius + 273.15
}

val temp = Temperature()
temp.celsius = 25.0
println("${temp.celsius}°C = ${temp.fahrenheit}°F = ${temp.kelvin}K")

temp.celsius = 100.0
println("${temp.celsius}°C = ${temp.fahrenheit}°F")

// try { temp.celsius = -300.0 } catch (e: Exception) { println("Error: ${e.message}") }

**المخرجات:

TEXT 📖 للعرض فقط
25.0°C = 77.0°F = 298.15K
100.0°C = 212.0°F

❓ أسئلة شائعة

س هل يمكن أن يتعايش المنشئ الأساسي والثانوي؟
ج نعم، لكن المنشآت الثانوية يجب أن تفوّض للأساسي باستخدام this(...). المعلمات الافتراضية مُفضَّلة على المنشآت الثانوية.
س هل يمكنني الحصول على كتل init متعددة؟
ج نعم، كتل init المتعددة تنفذ بترتيب الإعلان. يُوصى بدمجها في واحدة لتجنب الارتباك.
س هل يمكن استخدام lateinit للأنواع الأولية؟
ج لا. lateinit يعمل فقط مع الأنواع غير الأولية (أنواع الكائنات)، لأن الأولية لها قيم افتراضية. استخدم by lazy أو أنواع فارغة + قيم افتراضية لـ Int/Double إلخ.
س هل تهيئة by lazy آمنة للخيوط؟
ج نعم افتراضيًا (وضع SYNCHRONIZED). إذا كنت متأكدًا من الوصول بخيط واحد، استخدم LazyThreadSafetyMode.NONE لأداء أفضل.
س كيف تعمل رؤية internal في مشاريع Maven/Gradle متعددة الوحدات؟
ج internal يقيّد الرؤية داخل نفس وحدة Gradle أو وحدة Maven. وحدات مختلفة — حتى داخل نفس المشروع — لا يمكنها الوصول لأعضاء internal.
س لماذا لا يملك كوتلن package-private مثل جافا؟
ج كوتلن يستخدم internal (رؤية على مستوى الوحدة) بدلاً من package-private. الرؤية على مستوى الحزمة يُساء استخدامها غالبًا في جافا؛ الرؤية على مستوى الوحدة تناسب هياكل المشاريع الحديثة بشكل أفضل.

📖 ملخص


📝 تمارين

  1. أساسي (⭐): عرّف فئة Product (id: String, name: String, price: Double) مع كتلة init تتحقق من price >= 0. تلميح: require(price >= 0)
  2. متوسط (⭐⭐): صمم فئة Service تستخدم lateinit لحقن Repository وby lazy لتهيئة ذاكرة تخزين مؤقت. تلميح: lateinit + by lazy
  3. تحدي (⭐⭐⭐): نفّذ نموذج مجال طلب كامل مع Order/OrderItem/Customer، جميع التحققات في كتل init، والنسخ الدفاعي للعناصر. تلميح: راجع المثال الكامل في القسم 8

← السابق | التالي ←

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%