Menu
Coddy logo textTech

קבועים עם const

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Kotlin של Coddy. שיעור 14 מתוך 57.

const val מכריז על קבוע בזמן הידור: ערך שנקבע כשהתוכנית עוברת הידור. ניתן להשתמש בו ברמה העליונה של קובץ ובתוך object או companion object, ורק עבור טיפוסים בסיסיים כגון Int, Double ו-Boolean, ועבור String:

const val MAX_PLAYERS = 4

class Lobby {
    val players = mutableListOf<String>()

    fun canStart() = players.size in MIN_PLAYERS..MAX_PLAYERS

    companion object {
        const val MIN_PLAYERS = 2
    }
}

בתוך main:

val lobby = Lobby()
lobby.players += listOf("ada", "bo")
println(lobby.canStart())
println("${Lobby.MIN_PLAYERS}-$MAX_PLAYERS players")

פלט:

true
2-4 players

קבועים מקבלים שמות בפורמט UPPER_SNAKE_CASE. const מחמיר יותר מ-val: הערך חייב להיות ידוע בלי להריץ קוד, ולכן קריאה לפונקציה או רשימה אינן מותרות. גם val בתוך אובייקט משותף וקריא בלבד, ומקבל כל ערך:

const val GREETING = "Hello"                  // ליטרל מסוג String
const val TIMEOUT_MS = 30 * 1000              // מחושב מליטרלים
// const val START = System.nanoTime()        // שגיאה: לא קבוע
// const val DAYS = listOf("Mon", "Tue")      // שגיאה: רק טיפוסים בסיסיים ו-String
object Defaults {
    val DAYS = listOf("Mon", "Tue")           // val משותף לקריאה בלבד
}

אובייקט companion יכול להכיל גם מצב משותף שניתן לשינוי. כל מופע קורא ומשנה את אותה התכונה, ולכן הוא מתאים למונים ולמטמונים. המחלקה הזו שומרת מופע אחד לכל שם, כך שבקשה פעמיים מחזירה את אותו אובייקט (=== בודק ששתי הפניות מצביעות על אותו אובייקט):

class Tile private constructor(val name: String) {
    companion object {
        private val cache = mutableMapOf<String, Tile>()
        val created: Int
            get() = cache.size

        fun of(name: String) = cache.getOrPut(name) { Tile(name) }
    }
}

בתוך main:

val a = Tile.of("grass")
val b = Tile.of("grass")
Tile.of("water")
println(a === b)
println(Tile.created)

פלט:

true
2

ברמה העליונה או בתוך אובייקט נלווה? קבוע או פונקציה ברמה העליונה שייכים לקובץ שלהם, ומשתמשים בהם בשם הפשוט שלהם בכל קובץ בחבילה. שים קבוע בתוך אובייקט נלווה כשהוא שייך למחלקה אחת: מי שקוראים לו כותבים Lobby.MIN_PLAYERS, וכך מצוין מהיכן מגיע הערך, ו-private const val נשאר בתוך אותה מחלקה.

// Limits.kt
const val MAX_UPLOAD_MB = 25                 // משמש בכל מקום בתור MAX_UPLOAD_MB

// Lobby.kt
class Lobby {
    companion object {
        const val MIN_PLAYERS = 2            // משמש בתור Lobby.MIN_PLAYERS
        private const val ROOM_PREFIX = "L-" // רק בתוך Lobby
    }
}
challenge icon

אתגר

קל

השלם את המחלקה Order(val item: String, val qty: Int). האובייקט הנלווה שלה מכיל את הקבועים MAX_QTY = 10 ו־PREFIX = "ORD", מונה פרטי למספר ההזמנה הבאה, את הסכום המשותף לקריאה בלבד totalItems ואת count המחושב של ההזמנות שנוצרו. בלוק init דורש כמות בין 1 ל־MAX_QTY עם ההודעה quantity must be 1-10 (שנבנית מהקבוע), ואז מקצה להזמנה את המזהה שלה ORD-001, ORD-002, ... ומוסיף את הכמות שלה ל־totalItems. הפונקציה label() מחזירה ORD-001 tea x2.

הקוד שסופק מדפיס max per order: 10, ואז קורא שורות tea 2 ומדפיס את התווית או את הודעת השגיאה, ולבסוף מדפיס orders: 2, items: 5. הזמנות שנדחו אינן צורכות מספר.

הקוד שלך צריך להיות בקובץ Order.kt. הקובץ Main.kt מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.

נסו בעצמכם

fun main() {
    // קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
    val input = generateSequence(::readLine).toList()
    println("max per order: ${Order.MAX_QTY}")
    for (line in input) {
        val (item, qty) = line.split(" ")
        try {
            println(Order(item, qty.toInt()).label())
        } catch (e: IllegalArgumentException) {
            println(e.message)
        }
    }
    println("orders: ${Order.count}, items: ${Order.totalItems}")
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: Kotlin אונליין