我很难理解“支持财产”的目的

dis*_*stu 4 java kotlin

我现在正在学习 Kotlin。就上下文而言,我是一名 Java 开发人员已有 10 年以上了。

我偶然发现了支持属性的概念。据我了解,要解决的问题是:我在一个类中有一个属性。我希望此属性是可变的并且仅在包含的类中可见,因此我将其声明为private var. 该属性只能在包含的类中修改,但在类外应该是可读的。因此,Kotlin 文档提出了这样的建议:

private var _word = "test"
val word: String
   get() = _word
Run Code Online (Sandbox Code Playgroud)

这可以工作并满足上述要求,但是对于 Java 开发人员来说这看起来有点奇怪。在Java中,我们只需要1个字段(Java中没有属性这样的东西):

private String word = "test";

public String getWord() {
   return word;
}
Run Code Online (Sandbox Code Playgroud)

AFAIK,同样的结果。

今天我了解到,在 Kotlin 中,我可以将 setter 设为私有。那么这个 Kotlin 代码怎么样:

var word = "test"
     private set
Run Code Online (Sandbox Code Playgroud)

满足所有要求,代码更加简洁。

我错过了什么吗?为什么我需要支持财产?

gid*_*dds 6

(这个答案从一些有关 Kotlin 属性的一般信息开始,因为人们似乎对它们普遍感到困惑。)

\n

Kotlin 和 Java 之间的主要概念差异在于,在 Java 中,基本的东西是字段,而在 Kotlin 中,基本的东西是访问器方法

\n

因此,在 Java 中,您定义一个字段。\xe2\x80\x82 然后,如果需要,您可以为其定义显式 getter 和/或 setter 方法。\xe2\x80\x82该语言没有 \xe2\x80 的概念\x98property\xe2\x80\x99,也没有定义访问器和字段之间的任何显式连接;但是访问器方法名称(最初源自JavaBeans )有一个几乎每个人都遵循的长期约定。

\n

这很清楚,但很啰嗦。\xe2\x80\x82并且因为它只是一个约定,所以该语言不会强制您将字段与访问器相匹配:您可以拥有不同类型的访问器、没有 getter 的 setter 和反之亦然,甚至没有字段的访问器。\xe2\x80\x82(其中一些东西是可取的,但其他东西更可能导致错误和混乱!)

\n

然而,Kotlin 认为访问器才是重要的。\xe2\x80\x82(这是一个一般原则的示例,您应该针对接口进行编程,而不关心它是如何实现的。)\xe2 \x80\x82实际上,属性 就是它的访问器。\xe2\x80\x82只要你有 getter 方法,那么你就可以获得属性的值;只要你有一个setter方法,那么你也可以设置它。

\n

因为 getter 和 setter 通常都很琐碎,所以 Kotlin 会隐式定义它们,除非您另有说明。\xe2\x80\x82因此,当您编写时:

\n
val myProperty = 1\n
Run Code Online (Sandbox Code Playgroud)\n

然后 Kotlin 创建:

\n
    \n
  • 一个名为 的私有字段myProperty,其类型推断为Int,并初始化为值 1。
  • \n
  • 一个名为 的 getter 方法getMyProperty(),具有公共范围(默认情况下),返回一个Int.
  • \n
\n

这正是您在 Java 中所做的,只是更简洁。

\n

不同的是,每当您引用该属性时,您都会调用访问器方法,而不是直接访问该字段。\xe2\x80\x82(这就是为什么它被称为支持字段\ xe2\x80\x94 其唯一目的是供访问器使用。)

\n

当然,除非覆盖访问器,否则效果是相同的。\xe2\x80\x82例如:

\n
val myProperty = 1\n    get() {\n        LOG.info("Getting value of myProperty = $field")\n        return field\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

field是 getter 和 setter 中的特殊关键字,指的是它们的支持字段。)

\n

和之前一样,这会生成一个字段和一个 getter 方法;但 getter 方法是在代码中指定的,而不是自动生成的。

\n

(我没有提到可变属性或 setter,但原理是相同的:如果属性是 a var,那么您也会获得自动生成或显式 setter 方法。\xe2\x80\x82Kotlin没有的一种 Java 可能性\不支持只写属性,其中某些作用域可以看到 setter 但看不到 getter。\xe2\x80\x82 但这些很少使用;您始终可以使用普通方法来设置它们,这可能更清楚。)

\n

还有一种情况需要考虑。\xe2\x80\x82在 Java 中,getter 方法可以单独存在,无需相应的字段。\xe2\x80\x82(然后它必须从其他来源获取其值 \ xe2\x80\x94 可能通过对其他字段执行计算,或者从另一个对象获取它,甚至使用硬编码值。)

\n

您可以在 Kotlin 中执行相同的操作,方法是覆盖 getter(和 setter,如果可变)并且引用field,例如:

\n
val myProperty: Int\n    get() = someOtherObject.someOtherProperty\n
Run Code Online (Sandbox Code Playgroud)\n

在这种情况下,生成字段是没有意义的,因为它永远不会被使用!\xe2\x80\x82所以 Kotlin 识别这一点并只生成生成访问器方法。

\n

它仍然是一个属性,因为它仍然有访问器方法,其余代码看不到任何区别。\xe2\x80\x82它是否有支持字段纯粹是一个实现细节。

\n
\n

这个问题询问的是 \xe2\x80\x98backing属性\xe2\x80\x99.\xe2\x80\x82官方文档使用该术语来表示一个单独的私有属性,其中 \xe2\x80\x98shadows\xe2\x80\x99现有的,如本问题所示。\xe2\x80\x82(感谢 cactustictacs 提供参考。)

\n

在某些特定情况下,支持属性可能很有用。\xe2\x80\x82例如,假设您需要引用类中的特定类型,但仅公开公开超类型;私有财产可以是特定类型,然后公共财产可以是超类型。

\n

然而,这并不需要简单地限制 setter 方法的可见性:正如问题所示,这可以明确地完成(例如通过添加private set到属性定义中)。

\n

(根据我的经验,很少需要支持属性。)

\n