Kotlin 类中构造函数参数的声明方式

52 阅读3分钟

在 Kotlin 中,类构造函数的参数不一定要用 val 声明,也可以用 var,或者什么都不用。这取决于你是否希望这个参数成为类的属性(Property)。

这是 Kotlin 与 Java 一个很不一样的特性,理解它能让你写出更简洁、更安全的代码。

三种声明方式及其区别

让我们通过一个 Person 类的例子来详细说明这三种情况。

1. 使用 val 声明参数 (最常用)

class Person(val name: String, val age: Int) {
    // ...
}
  • 效果:这会自动创建一个 只读(read-only)  的类属性 name 和 age

  • 幕后发生的事

    1. 声明了一个 private final 的字段(field)来存储 name 和 age 的值。
    2. 提供了一个 public 的 getter 方法(getName() 和 getAge())来读取这个字段的值。
    3. 在构造函数中,将传入的参数值赋给这个字段。
  • 如何使用:你可以通过对象实例来访问这个属性,例如 person.name。但你不能修改它,person.name = "New Name" 会编译报错。

  • 适用场景:当你希望构造函数的参数成为对象的状态,并且这个状态在对象生命周期内不应该被改变时,就应该使用 val。这是最推荐的做法,因为它能保证对象的不可变性(Immutability) ,让代码更安全、更易于维护。

2. 使用 var 声明参数

class Person(var name: String, var age: Int) {
    // ...
}
  • 效果:这会自动创建一个 可读写(read-write)  的类属性 name 和 age

  • 幕后发生的事

    1. 声明了一个 private 的字段来存储 name 和 age 的值。
    2. 提供了 public 的 getter 和 setter 方法。
  • 如何使用:你既可以读取也可以修改这个属性。

    val person = Person("Alice", 30)
    println(person.name) // 读取,输出 "Alice"
    person.name = "Bob"  // 修改
    println(person.name) // 输出 "Bob"
    
  • 适用场景:当你希望构造函数的参数成为对象的状态,并且这个状态在对象生命周期内可能需要被改变时,使用 var

3. 不使用 val 或 var 声明参数

class Person(name: String, age: Int) {
    // ...
}
  • 效果:这个参数不会成为类的属性。它仅仅是一个构造函数的参数,作用域仅限于构造函数内部和初始化代码块(init 块)。

  • 幕后发生的事:不会自动创建任何字段或 getter/setter。这个参数就像一个普通函数的参数一样,在构造函数执行完毕后就会被销毁。

  • 如何使用:你不能在类的其他方法中直接访问 name 或 age

    class Person(name: String, age: Int) {
        init {
            // 可以在 init 块中访问
            println("初始化一个 $name 的对象")
        }
        
        fun introduce() {
            // 编译错误!Cannot access 'name': it is not a member of 'Person'
            // println("My name is $name") 
        }
    }
    
  • 适用场景

    • 当你只需要在构造函数执行期间(比如在 init 块中)使用这个参数的值,而不需要将它作为对象的状态保存下来时。

    • 你可能会用它来计算或初始化其他真正的类属性。例如:

      class Person(fullName: String, age: Int) {
          val firstName: String
          val lastName: String
          
          init {
              val parts = fullName.split(" ")
              firstName = parts[0]
              lastName = parts[1]
          }
          // 现在可以在类的其他地方访问 firstName 和 lastName 了
      }
      
    • 注意:如果主构造函数没有任何注解或可见性修饰符,你也可以省略 constructor 关键字。

总结

声明方式是否成为类属性可变性适用场景
val只读 (不可变)推荐。参数作为对象的状态,且一旦初始化就不应改变。
var可读写 (可变)参数作为对象的状态,且可能在对象生命周期中改变。
无关键字- (仅为构造函数参数)参数仅在构造函数或 init 块中使用,不作为对象的状态保存。

核心建议:在绝大多数情况下,你应该使用 val,因为它能带来不可变性,让你的代码更健壮、更易于推理。只有在确实需要修改属性时,才使用 var