为什么属性初始值设定项不会调用自定义setter?

bre*_*dan 15 kotlin

Kotlin文档中,允许使用自定义setter:

class Test {
  var stringRepresentation: String
    get() = field
    set(value) {
      setDataFromString(value) 
    }

  init {
    stringRepresentation = "test"
  }

  private fun setDataFromString(value: String) { }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果没有自定义getter(并从init块中初始化),则无法拥有自定义setter :

class Test {
  // Compilation error: "Property must be initialized"
  var stringRepresentation: String
    set(value) {
      setDataFromString(value)
    }

  init {
    stringRepresentation = "test"
  }

  private fun setDataFromString(value: String) { }
}
Run Code Online (Sandbox Code Playgroud)

虽然你可以有一个没有自定义setter的自定义getter,但这里没问题:

class Test {
  var stringRepresentation: String
    get() = field 

  init {
    stringRepresentation = "test"
  }

  private fun setDataFromString(value: String) { }
}
Run Code Online (Sandbox Code Playgroud)

那么为什么你不能使用自定义setter和从init块中初始化的属性,为什么init块在属性初始化程序直接分配时调用自定义setter,绕过自定义setter?

class Test {
  var stringRepresentation: String = "" // Does not call custom setter
    set(value) {
      setDataFromString(value)
    }

  init {
    stringRepresentation = "test" // Calls custom setter
  }

  private fun setDataFromString(value: String) { }
}
Run Code Online (Sandbox Code Playgroud)

Jan*_*son 6

属性初始值设定项不会调用自定义setter,因为它们的目的是提供默认值.

与Java不同,在Kotlin中,不仅必须在第一次访问之前初始化局部变量,还必须初始化类属性.

在Java中,这是有效的.

public class Test {
    public String str;

    public static void main(String[] args) {
        System.out.println(new Test().str);
    }
}
Run Code Online (Sandbox Code Playgroud)

在Kotlin,这不是.

class Parent {
    var str: String?
}

fun main(args: Array<String>) {
    Parent().str
}
Run Code Online (Sandbox Code Playgroud)

因此,自定义setter需要通过属性初始值设定项或构造函数初始化其属性.看一下下面的例子.

class Test {
    var stringRepresentation: String = "a" // Default value. Does not call custom setter
        get() = field
        set(value) {
            println("Setting stringRepresentation property to %s. Current value is %s.".format(value, field))
            field = setDataFromString(value)
        }

    init {
        this.stringRepresentation = "b" // Calls custom setter
    }

    private fun setDataFromString(value: String): String {
        println("Setting stringRepresentation property to %s.".format(value))
        return value
    }
}

fun main(args: Array<String>) {
    Test().stringRepresentation = "c" // Calls custom setter
}
Run Code Online (Sandbox Code Playgroud)

属性stringRepresentation初始化为其类的"a" opon实例,而不调用setter.然后调用init块并使用setter 将值设置为"b".然后使用setter 来"c".

  • 只是难以理解为什么添加带有`init`块属性初始化的自定义setter将无法编译(因为属性*正被初始化).有没有办法避免初始化程序和自定义setter中的代码重复,而无需委托给另一个方法?我想在初始化期间做一些工作,避免两次写入属性.每当调用`set`时,都需要完成同样的工作. (4认同)
  • Kotlin 似乎是一门不错的语言,但在某些地方,它离不开一勺狗屎...... ((( (3认同)
  • @breandan,您可以通过将值初始化为某个随机值来避免重复,只是为了满足此要求。然后在构造函数初始化程序块中,您可以通过设置属性值来实际初始化属性值,这将调用自定义设置器。例如,您可以在属性初始值设定项中将 String 初始化为 "",然后在属性下方放置一个 `init {}` 块,该块使用传递到构造函数的值来再次设置该属性,从而调用 setter 代码。尽管这可行,但这似乎是该语言设计中的一个缺陷。 (2认同)