Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

属性

属性是属于类的 varval

定义属性可以在类内部维护状态。维护状态是创建类的主要原因,而不仅仅是编写一个或多个独立函数。

var 属性可以重新赋值,而 val 属性则不能。每个对象都有自己的属性存储:

// Properties/Cup.kt

class Cup {
  var percentFull = 0
}

fun main() {
  val c1 = Cup()
  c1.percentFull = 50
  val c2 = Cup()
  c2.percentFull = 100

  println(c1.percentFull)
  println(c2.percentFull)
}
/* 输出:
50
100
*/

在类内部定义 varval 属性的方式与在函数内部定义它们的方式非常相似。然而,varval 将成为该类的一部分,您必须通过点表示法来引用它,即在对象和属性名称之间放置一个点。您可以在每次引用 percentFull 时看到点表示法的用法。

percentFull 属性表示相应的 Cup 对象的状态。c1.percentFullc2.percentFull 包含不同的值,显示每个对象都有自己的存储。

成员函数可以在其对象内部引用属性,而无需使用点表示法(即限定):

// Properties/Cup2.kt

class Cup2 {
  var percentFull = 0
  val max = 100
  fun add(increase: Int): Int {
    percentFull += increase
    if (percentFull > max)
      percentFull = max
    return percentFull
  }
}

fun main() {
  val cup = Cup2()
  cup.add(50)
  println(cup.percentFull)
  cup.add(70)
  println(cup.percentFull)
}
/* 输出:
50
100
*/

add() 成员函数试图将 increase 添加到 percentFull,但确保它不会超过 100%。

在类外部,您必须限定类的属性和成员函数。

您可以定义顶层属性:

// Properties/TopLevelProperty.kt

val constant = 42

var counter = 0

fun inc() {
  counter++
}

定义顶层 val 是安全的,因为它不能被修改。然而,定义可变(var)的顶层属性被认为是一种反模式。随着程序变得越来越复杂,正确推理共享可变状态变得越来越困难。如果代码库中的每个人都可以访问 var counter,您无法保证它会正确地发生变化:尽管 inc()counter 增加一,但程序的其他某个部分可能将 counter 减少十,导致出现难以理解的错误。最好将可变状态限制在类内部。在限制可见性中,您将看到如何使其真正隐藏。

var 可以更改,而 val 不能更改是一种过于简单化的说法。打个比方,将 house 视为 val,而位于 house 内部的 sofa 视为 var。您可以修改 sofa,因为它是 var。但是,您不能重新分配 house,因为它是 val

// Properties/ChangingAVal.kt

class House {
  var sofa: String = ""
}

fun main() {
  val house = House()
  house.sofa = "Simple sleeper sofa: $89.00"
  println(house.sofa)
  house.sofa = "New leather sofa: $3,099.00"
  println(house.sofa)
  // 不能重新分配 val 到新的 House:
  // house = House()
}
/* 输出:
Simple sleeper sofa: $89.00
New leather sofa: $3,099.00
*/

尽管 houseval,但其对象可以修改,因为 class House 中的 sofavar。将 house 定义为 val 只会防止将其重新分配给新对象。

如果将属性定义为 val,则不能重新分配它:

// Properties/AnUnchangingVar.kt

class Sofa {
  val cover: String = "Loveseat cover"
}

fun main() {
  var sofa = Sofa()
  // 不允许:
  // sofa.cover = "New cover"
  // 重新分配一个 var:
  sofa = Sofa()
}

尽管 sofavar,但其对象无法修改,因为 class Sofa 中的 coverval。然而,sofa 可以重新分配给新对象。

我们谈到了诸如 housesofa 之类的标识符,好像它们是对象一样。实际上,它们是对象的引用。看待这一点的一种方式是观察两个标识符可以引用同一个对象:

// Properties/References.kt

class Kitchen {
  var table: String = "Round table"
}

fun main() {
  val kitchen1 = Kitchen()
  val kitchen2 = kitchen1
  println("kitchen1: ${kitchen1.table}")
  println("kitchen2: ${kitchen2.table}")
  kitchen1.table = "Square table"
  println("kitchen1: ${kitchen1.table}")
  println("kitchen2: ${kitchen2.table}")
}
/* 输出:
kitchen1: Round table
kitchen2: Round table
kitchen1: Square table
kitchen2: Square table

kitchen1 修改 table 时,kitchen2 也会看到修改。kitchen1.tablekitchen2.table 显示相同的输出。

请记住,varval 控制的是引用,而不是对象。var 允许您将引

用重新绑定到不同的对象,而 val 则会防止您这样做。

可变性意味着对象可以更改其状态。在上述示例中,class Houseclass Kitchen 定义了可变对象,而 class Sofa 定义了不可变对象。

练习和解答可以在 www.AtomicKotlin.com 找到。