在 Kotlin 中的 Getter 和 Setter

139
在Java中,例如,我可以自己编写getter方法(由IDE生成)或使用注释,如lombok中的@Getter - 这很简单。
然而,在Kotlin中,默认情况下具有getter和setter。但我不知道如何使用它们。
我想让它与Java类似:
private val isEmpty: String
        get() = this.toString() //making this thing public rises an error: Getter visibility must be the same as property visibility.

那么getter如何工作?
8个回答

215

Kotlin会自动生成Getter和Setter。如果你写:

val isEmpty: Boolean

它等同于以下Java代码:

private final Boolean isEmpty;

public Boolean isEmpty() {
    return isEmpty;
}

在您的情况下,private 访问修饰符是多余的 - isEmpty默认为private,并且只能通过getter访问。当您尝试获取对象的isEmpty属性时,实际上会调用get方法。要了解有关Kotlin中getter / setter的更多理解:下面的两个代码示例是相等的:

在您的情况下,private 访问修饰符是多余的 - isEmpty默认为private,并且只能通过getter访问。当您尝试获取对象的isEmpty属性时,实际上会调用get方法。要了解有关Kotlin中getter / setter的更多理解:下面的两个代码示例是相等的:

var someProperty: String = "defaultValue"
and
var someProperty: String = "defaultValue"
    get() = field
    set(value) { field = value }

另外,我想指出,在 getter 方法中的 this 不是您的属性 - 它是类实例。如果您想要在 getter 或 setter 中访问字段的值,可以使用保留关键字 field

val isEmpty: Boolean
  get() = field

如果您只想要一个公共访问的get方法-可以编写此代码:

var isEmpty: Boolean
    private set 

由于 set 访问器旁边的 private 修饰符,您只能在对象内部的方法中设置此值。


35
在您的情况下,私有访问修饰符是多余的。为什么?Kotlin文档指出默认修饰符是public。https://kotlinlang.org/docs/reference/visibility-modifiers.html - user6685522
@Nothing 是的,它看起来像是公共字段,但在底层你调用了getter方法。 - Cortwave
“val isEmpty: Boolean” 这段代码无法编译,因为isEmpty尚未初始化,对吧?我刚开始学习Kotlin。另外,“get() = field”这段代码是什么意思? - Shubham A.
如果您想使用带有私有setter的可观察对象,该怎么办?例如:当设置值时,您可以执行一些反应式逻辑。 - Marchy
这与您提到的代码不相等。生成的getter/setter是final的,在访问器在扩展代理类中被覆盖的情况下会有所不同。例如,Hibernate经常使用这种方法。 - aleksey.stukalov
显示剩余4条评论

36

有关属性访问器可见性修饰符的规则如下:

  • Getter visibility of var and val property should be exactly the same to the visibility of the property, thus you can only explicitly duplicate the property modifier, but it is redundant:

    protected val x: Int
        protected get() = 0 // No need in `protected` here.
    
  • Setter visibility of var property should be the same or less permissive than the property visibility:

    protected var x: Int
        get() = 0
        private set(x: Int) { } // Only `private` and `protected` are allowed.
    
在Kotlin中,属性总是通过getter和setter访问的,因此不需要像Java一样使用公共访问器使属性私有--如果存在,则其支持字段已经是私有的。因此,属性访问器上的可见性修饰符仅用于使setter的可见性更少:
  • For a property with backing field and default accessors:

    var x = 0 // `public` by default
        private set
    
  • For a property without backing field:

    var x: Int // `public` by default
        get() = 0
        protected set(value: Int) { }
    

能否设置和获取不同类型的值?例如将 x 设置为 "Some String" 并返回字符串的长度,即 11 - Carel
@Carel,目前还不支持这种用例:属性访问器必须与属性类型完全一致。使用不同类型的方式是通过使用单独的后备属性来实现的。 - hotkey
天啊,Kotlin和Python如此之相似,以至于你认为它们会像Python一样工作,但是它却有类型限制...结果自己搞砸了。 - Carel
感谢访问修饰符。我从一个变量中删除了“private”,现在可以通过getter从另一个类中访问它了。 - CoolMind
当我使用 "var x//private set" 组合时,会得到一个 "不允许在公有属性中使用私有设置器" 的错误。将其声明为 "final var x" 可以解决该问题。 - Tom

27

Kotlin中属性firstName的默认settergetter示例:

1) 示例默认 settergetter for property firstName in Kotlin

class Person {
    var firstName: String = ""
            get() = field       // field here ~ `this.firstName` in Java or normally `_firstName` is C#
            set(value) {
                field = value
            }

}

使用

val p = Person()
p.firstName = "A"  // access setter
println(p.firstName) // access getter (output:A)

如果您的settergetter与以上内容完全相同,则可以删除它,因为它是不必要的

2)示例自定义setter和getter。

const val PREFIX = "[ABC]"

class Person {

    // set: if value set to first name have length < 1 => throw error else add prefix "ABC" to the name
    // get: if name is not empty -> trim for remove whitespace and add '.' else return default name
    var lastName: String = ""
        get() {
            if (!field.isEmpty()) {
                return field.trim() + "."
            }
            return field
        }
        set(value) {
            if (value.length > 1) {
                field = PREFIX + value
            } else {
                throw IllegalArgumentException("Last name too short")
            }
        }
}

使用

val p = Person()
p.lastName = "DE         " // input with many white space
println(p.lastName)  // output:[ABC]DE.
p.lastName = "D" // IllegalArgumentException since name length < 1

更多信息
我从Java开始学习Kotlin,所以我对fieldproperty感到困惑,因为在Java中没有property
经过一些搜索,我发现Kotlin中的fieldproperty看起来像C# (What is the difference between a field and a property?)

这里有一些相关的帖子,谈论Java和Kotlin中的fieldproperty
does java have something similar to C# properties?
https://blog.kotlin-academy.com/kotlin-programmer-dictionary-field-vs-property-30ab7ef70531

如果我错了,请纠正我。希望这有所帮助。


谢谢,这真的帮了我很多! - marcode_ely

10

Kotlin中的Getter默认为public,但您可以将Setter设置为private,并使用类内的一个方法设置值。像这样。

/**
* Created by leo on 17/06/17.*/

package foo
class Person() {
var name: String = "defaultValue"
               private set

fun foo(bar: String) {
    name = bar // name can be set here
       }
}

fun main(args: Array<String>) {
  var p = Person()
  println("Name of the person is ${p.name}")
  p.foo("Jhon Doe")
  println("Name of the person is ${p.name}")
}

9

如果您有一个变量,则可以执行以下操作:

var property: String = "defVal"
              get() = field
              set(value) { field = value }

但在Val的情况下,一旦分配就无法设置,因此不会有setter块:

val property: String = "defVal"
              get() = field

或者如果您不想使用setter:

val property: String = "defVal"
              private set

在将Java转换为Kotlin时要小心,因为AI引擎通常会将Java变量的setter和getter移动到Kotlin的自己函数中,例如getProperty()。这将导致各种各样的错误。您需要实现Ali发布的解决方案。 - Michael N

6
您可以查看这个教程以获取更多信息:

另一个适用于Android开发人员的Kotlin教程

Properties

In Kotlin world, classes cannot have fields, just properties. var keyword tells us the property is mutable, in contrast to val. Let’s see an example:

class Contact(var number: String) {

   var firstName: String? = null
   var lastName: String? = null
   private val hasPrefix : Boolean
       get() = number.startsWith("+")

}

There is not much code, but lots of things are happening behind the scenes. We will go through it step by step. First of all, we created a public final class Contact.

This is the primary rule we have to face: if not specified otherwise, classes are public and final by default (by the way, the same is for class methods). If you want to inherit from class, mark it with open keyword.


0
有点不同意@Cortwave的最佳答案。 Kotlin成员字段默认情况下是final,而不是private。您必须编写private修饰符使其封装在类中。
我在这里寻找的答案是Kotlin是否自动生成私有成员字段的getter和setter,还是您必须自己编写它?在Android Studio中进行直接实现表明您需要自己编写它们。

0
这里有一个实际的、现实世界中的 Kotlin Getter 和 Setter 示例(更多细节请参见这里):
// Custom Getter
val friendlyDescription get(): String {
    val isNeighborhood = district != null
    var description = if (isNeighborhood) "Neighborhood" else "City"
    description += " in"
    if (isNeighborhood) {
        description += " $city,"
    }
    province?.let {
        if (it.isNotEmpty()) {
            description += " $it,"
        }
    }
    description += " $country"
    return description
}

print(myLocation.friendlyDescription) // "Neighborhood in Denver, Colorado, United States"


// Custom Setter
enum class SearchResultType {
    HISTORY, SAVED, BASIC
}

private lateinit var resultTypeString: String

var resultType: SearchResultType
    get() {
        return enumValueOf(resultTypeString)
    }
    set(value) {
        resultTypeString = value.toString()
    }

result.resultType = SearchResultType.HISTORY
print(result.resultTypeString) // "HISTORY"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接