ZetCode

Kotlin private 关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的访问修饰符控制类、对象和成员的可见性。private 关键字是最严格的修饰符。本教程将通过实际例子深入探讨 private 关键字。

基本定义

Kotlin 中的 private 关键字将可见性限制为声明作用域。私有成员只能在同一类或文件中访问。这通过隐藏实现细节来促进封装。

私有属性

私有属性只能在其声明的类中访问。这可以防止外部代码直接修改内部状态。

PrivateProperty.kt
package com.zetcode

class BankAccount {
    private var balance: Double = 0.0
    
    fun deposit(amount: Double) {
        if (amount > 0) {
            balance += amount
        }
    }
    
    fun getBalance(): Double = balance
}

fun main() {
    val account = BankAccount()
    account.deposit(100.0)
    println(account.getBalance()) // Output: 100.0
    
    // account.balance = 500.0 // Error: Cannot access 'balance'
}

在这里,balance 是私有的,只能通过 deposit 方法修改。禁止从类外部直接访问,确保对余额进行受控修改。

私有函数

私有函数是仅在其声明的类中可访问的辅助方法。它们有助于将复杂的操作分解为更小的步骤。

PrivateFunction.kt
package com.zetcode

class Calculator {
    fun add(a: Int, b: Int): Int {
        validateInput(a, b)
        return a + b
    }
    
    private fun validateInput(a: Int, b: Int) {
        require(a >= 0 && b >= 0) { 
            "Inputs must be non-negative" 
        }
    }
}

fun main() {
    val calc = Calculator()
    println(calc.add(5, 3)) // Output: 8
    
    // calc.validateInput(2, 2) // Error: Cannot access 'validateInput'
}

validateInput 函数是私有的,由 add 方法内部使用。外部代码无法直接调用此验证函数,从而保持验证逻辑的封装性。

私有构造函数

私有构造函数可防止直接实例化类。这对于单例模式或工厂方法很有用。

PrivateConstructor.kt
package com.zetcode

class Database private constructor() {
    companion object {
        private var instance: Database? = null
        
        fun getInstance(): Database {
            if (instance == null) {
                instance = Database()
            }
            return instance!!
        }
    }
    
    fun query(sql: String) = println("Executing: $sql")
}

fun main() {
    val db = Database.getInstance()
    db.query("SELECT * FROM users")
    
    // val db2 = Database() // Error: Cannot access ''
}

私有构造函数强制客户端使用 getInstance 获取一个 Database 对象。这确保只存在一个实例(单例模式)。实例变量也是私有的,以防止外部修改。

顶级声明中的私有

当用于顶级声明(在任何类之外)时,private 将可见性限制为当前文件。这对于文件特定的实用程序很有用。

TopLevelPrivate.kt
package com.zetcode

private const val MAX_RETRIES = 3

private fun logError(message: String) {
    println("ERROR: $message")
}

class NetworkClient {
    fun fetchData() {
        var attempts = 0
        while (attempts < MAX_RETRIES) {
            try {
                // Network operation
                return
            } catch (e: Exception) {
                logError(e.message ?: "Unknown error")
                attempts++
            }
        }
    }
}

// In another file:
// fun test() {
//     println(MAX_RETRIES) // Error: Cannot access 'MAX_RETRIES'
//     logError("Test") // Error: Cannot access 'logError'
// }

常量 MAX_RETRIES 和函数 logError 都对该文件是私有的。它们可以在文件中使用,但无法从其他文件访问,即使在同一包中也是如此。

私有 Setter

属性可以具有私有 Setter,同时保持 Getter 为公共。这允许对外部代码进行只读访问,同时允许内部修改。

PrivateSetter.kt
package com.zetcode

class User(name: String) {
    var name: String = name
        private set
    
    fun changeName(newName: String) {
        if (newName.isNotBlank()) {
            name = newName
        }
    }
}

fun main() {
    val user = User("Alice")
    println(user.name) // Output: Alice
    
    user.changeName("Bob")
    println(user.name) // Output: Bob
    
    // user.name = "Charlie" // Error: Cannot assign to 'name'
}

name 属性具有公共 Getter,但具有私有 Setter。外部代码可以读取名称,但只能通过 changeName 方法修改它,该方法可以强制执行验证规则。

嵌套类中的私有

嵌套类中的私有成员的行为类似于常规类,可见性仅限于嵌套类作用域。

NestedClassPrivate.kt
package com.zetcode

class Outer {
    private val outerSecret = "Outer secret"
    
    inner class Inner {
        private val innerSecret = "Inner secret"
        
        fun revealSecrets() {
            println(outerSecret) // Can access outer's private members
            println(innerSecret)
        }
    }
    
    fun testInner() {
        val inner = Inner()
        inner.revealSecrets()
        // println(inner.innerSecret) // Error: Cannot access 'innerSecret'
    }
}

fun main() {
    val outer = Outer()
    outer.testInner()
    
    // println(outer.outerSecret) // Error: Cannot access 'outerSecret'
}

内部类可以访问其外部类的私有成员,但内部类的私有成员无法被外部类访问。两个类的私有成员都无法从 Outer 类外部访问。

接口中的私有

从 Kotlin 1.4 开始,Kotlin 接口可以具有私有函数。这些函数只能在接口及其实现中使用。

InterfacePrivate.kt
package com.zetcode

interface Logger {
    fun log(message: String)
    
    private fun formatMessage(message: String): String {
        return "[${System.currentTimeMillis()}] $message"
    }
    
    fun logWithTimestamp(message: String) {
        log(formatMessage(message))
    }
}

class ConsoleLogger : Logger {
    override fun log(message: String) {
        println(message)
    }
}

fun main() {
    val logger = ConsoleLogger()
    logger.logWithTimestamp("Hello, Kotlin!")
    
    // logger.formatMessage("Test") // Error: Cannot access 'formatMessage'
}

formatMessage 函数对 Logger 接口是私有的。它可以被其他接口成员(如 logWithTimestamp)使用,但无法被实现类或外部代码访问。

private 关键字的最佳实践

来源

Kotlin 可见性修饰符文档

本教程深入介绍了 Kotlin 的 private 关键字,展示了各种应用,包括属性、函数、构造函数和嵌套类。正确使用私有可见性可以促进封装,并通过隐藏实现细节来创建更易于维护的代码。

作者

我的名字是 Jan Bodnar,我是一位热情的程序员,拥有多年的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我写了 1400 多篇文章和 8 本电子书。我有超过八年的编程教学经验。

列出 所有 Kotlin 教程