【问题标题】:Private getter and public setter for a Kotlin propertyKotlin 属性的私有 getter 和 public setter
【发布时间】:2016-07-07 10:23:41
【问题描述】:

如何在 Kotlin 中创建一个具有私有 getter(或没有它)但具有公共 setter 的属性?

var status
private get

无法正常工作,出现错误:Getter visibility must be the same as property visibility

就我而言,原因是 Java 互操作:我希望我的 Java 代码能够调用 setStatus,但不能调用 getStatus

【问题讨论】:

  • 只是好奇:你为什么要这么做?只写字段?
  • private get的意思是直接在类中使用属​​性,不是吗?
  • 是的。只写字段。它只能从类内部读取。
  • 这是个好问题。我很好奇写但不读字段的用例?我想不出你会需要它不可读的原因。你能分享你的用例吗?
  • @aaaidan 这将是构建器和命令设计模式的典型用例。

标签: java kotlin kotlin-interop


【解决方案1】:

目前在 Kotlin 中不可能有一个带有比该属性更可见的 setter 的属性。问题跟踪器中有一个语言设计问题,请随时观看/投票或分享您的用例:https://youtrack.jetbrains.com/issue/KT-3110

【讨论】:

  • 说 _ 在 Kotlin 中拥有一个比属性更可见的 setter 的属性与说 getter 不能比 getter 更可见 是一样的吗?
【解决方案2】:

在当前的 Kotlin 版本 (1.0.3) 中,唯一的选择是拥有单独的 setter 方法,如下所示:

class Test {
    private var name: String = "name"

    fun setName(name: String) {
        this.name = name
    }
}

如果您希望限制外部库访问 getter,您可以使用 internal 可见性修饰符,允许您仍然在库中使用属性语法:

class Test {
    internal var name: String = "name"
    fun setName(name: String) { this.name = name }
}

fun usage(){
    val t = Test()
    t.name = "New"
}

【讨论】:

  • 如果你打算让 Java 使用代码,作为附加组件,internal 关键字最终会在 Java 世界中公开
【解决方案3】:

具有编译时错误的只写属性可以从 Kotlin 1.0 开始使用基于@Deprecated 的解决方法来实现。

实施

Kotlin 允许使用级别 ERROR 标记已弃用的函数,这会在调用时导致编译时错误。将属性的 get 访问器注释为 error-deprecated,并结合支持字段(以便仍然可以进行私有读取),可以实现所需的行为:

class WriteOnly {
    private var backing: Int = 0

    var property: Int
        @Deprecated("Property can only be written.", level = DeprecationLevel.ERROR)
        get() = throw NotImplementedError()
        set(value) { backing = value }

    val exposed get() = backing // public API
}

用法:

val wo = WriteOnly()
wo.property = 20         // write: OK

val i: Int = wo.property // read: compile error
val j: Int = wo.exposed  // read value through other property

编译错误也很有帮助:

使用“getter for property: Int”是一个错误。属性只能写。


用例

  1. 主要用例显然是允许写入但不能读取属性的 API:

    user.password = "secret"
    val pw = user.password // forbidden
    
  2. 另一种情况是修改内部状态的属性,但不将自身存储为字段。 (可以使用不同的设计更优雅地完成)。

    body.thrust_force = velocity
    body.gravity_force = Vector(0, 0, 9.8)
    // only total force accessible, component vectors are lost
    val f = body.forces
    
  3. 这种模式也适用于以下类型的 DSL:

    server {
        port = 80
        host = "www.example.com"
    }
    

    在这种情况下,值仅用作一次性设置,此处描述的只写机制可以防止意外读取属性(可能尚未初始化)。


限制

由于此功能并非针对此用例而设计,因此具有一定的局限性:

  • 如果使用属性引用访问,编译时错误会变成运行时错误:

    val ref = wo::property
    val x = ref.get() // throws NotImplementedError
    
  • 反射也是如此。

  • 此功能不能外包给委托,因为 错误已弃用 getValue() 方法不能与 by 一起使用。

【讨论】:

  • 这很有趣,也很有创意,但作为一个 hack 感觉不对。知道你能做到这一点很有趣,但是我建议不要将这个技巧引入生产代码。尤其是因为它在清晰度和简洁性方面并没有真正击败“幼稚”的自定义设置器替代方案。
  • 我同意声明网站的清晰/简洁,因为这不是已知的方法。然而,在调用现场,情况可能正好相反——getX/setX 方法更冗长,并且可能会提出“为什么没有属性?”的问题,这在 Kotlin 中是惯用的。一个不错但很小的副作用:如果 Kotlin 曾经引入只写属性,那么调用站点语法就已经是正确的;)
猜你喜欢
  • 1970-01-01
  • 2017-06-27
  • 1970-01-01
  • 2018-08-06
  • 2021-01-08
  • 1970-01-01
  • 1970-01-01
  • 2018-07-16
相关资源
最近更新 更多