为什么 Kotlin 隐式调用属性的 getter 和 setter?

not*_*Dev 1 kotlin

当您尝试访问属性时,Kotlin 隐式调用属性的 getter/setter 函数的目的是什么?getter 和 setter 的重点不是已经是如果您打算使用它们就可以轻松调用它们吗?Kotlin 的版本基本上只是引入了“字段”标识符的额外复杂性,并引入了如下所示的奇怪之处,其中对象的行为可能不像其接口预期的那样:

interface Counter {
    var count: Int
    fun increment() {
        count = count + 1
    }
}

class WeirdCounter: Counter {
    override var count: Int = 0
        get() = field
        set(value) {println("ignore the value")}
}
Run Code Online (Sandbox Code Playgroud)

只是想了解这背后的意图。

gid*_*dds 5

Kotlin 使用 getter 和 setter 实现属性的方式基本上是常见做法 \xe2\x80\x94 以及许多其他语言中的最佳实践 \xe2\x80\x94 。

\n

\xe2\x80\x98Bare\xe2\x80\x99 字段与 Java 中一样,简单、清晰、易于使用;但裸露的田地有问题

\n
    \n
  • 它们公开了实现细节(字段,尤其是其类型),防止它在将来被更改。

    \n
  • \n
  • 他们不允许类控制自己的状态。

    \n
  • \n
\n

当然,对于简单的值类来说,这些不是问题。\xc2\xa0 但对于更复杂的类,它们可能是一个真正的问题。

\n

例如,您可能想要更改类存储其状态的方式(例如,将 a 替换long为 a BigDecimal),但如果该类是流行库的公共接口的一部分,那么成千上万的用户会感到非常恼火。

\n

或者假设如果您可以确保 String 属性始终以小写形式存储而没有前导或尾随空格,那将非常方便。 \xc2\xa0 但是对于 \xe2\x80\x98bare\xe2\x80\x99 属性,则没有强制执行的方法。

\n

因此,通常的模式是有一个私有字段,并且只能从类本身(您控制的)内部访问;并提供访问器方法

\n

这使您可以完全控制。\xc2\xa0 只要您更新访问器方法以根据需要与新形式进行转换,就可以更改内部表示形式。\xc2\xa0 并且您的设置器可以执行任何规范化、格式化、或任何对国家实施任何限制的措施。

\n

然而,在像 Java 这样的语言中,这比简单的字段更尴尬和冗长:访问器方法将一行字段变成七行(不包括空行,也不包括文档注释,所以这可能更像是把 3 行变成 21 行) ).\xc2\xa0 虽然调用 getter 方法只比引用字段长几个字符(使用get()),但调用 setter 比简单的赋值要直观得多。

\n

结果是,开发人员要么做正确的事情,并用样板文件填充他们的类(包含对可维护性和错误风险的所有影响),要么他们不打扰并冒上述问题的风险。

\n

不过,Kotlin 提供了两全其美的优点:一个简单的属性在定义和访问它时看起来都像一个字段。\xc2\xa0 所以你会得到精简、简洁、清晰的代码。\xc2\xa0 但它已实现具有私有支持字段(如果需要)和访问器方法,因此您也可以获得这些的所有优点。\xc2\xa0 如果您需要添加验证或更改表示或记录所有访问或其他任何内容,您可以选择用您自己的实现替换默认访问器。

\n

你的WeirdCounter例子很奇怪,但并不像你想象的那么可怕(或可能)。\xc2\xa0 在面向对象的语言中,一个类是其自身状态的主人,而其他类通常不会也不应该这样\xc2\xa0 (这样,它们就不会受到这些内部结构的更改。)\xc2\xa0 如果一个类需要在 setter 中做一些违反直觉的事情,那么只有在它破坏了类的契约 \xe2\x80\x94 但这将是一个错误,并且应该在测试中变得明显,如果不是在其他地方的话。

\n

在实践中,类控制对其状态的访问的能力比类使用该能力做一些愚蠢或恶意的事情(这很容易发现)的风险更重要。

\n