Kotlin:在构造函数中初始化类属性

Chr*_*her 16 constructor class-attribute kotlin

我创建了一个带有class属性的Kotlin类,我想在构造函数中初始化它:

public class TestClass {

    private var context : Context? = null // Nullable attribute

    public constructor(context : Context) {
       this.context = context
    }

    public fun doSomeVoodoo() {
       val text : String = context!!.getString(R.string.abc_action_bar_home_description)
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我必须使用"?"将属性声明为Nullable.符号,虽然该属性将在构造函数中初始化.将此属性声明为Nullable属性使得始终需要强制使用"!!"的NonNull值 或者用"?"提供空检查.

有没有办法避免这种情况,如果class属性将在构造函数中初始化?我想欣赏像这样的解决方案:

public class TestClass {

    private var context : Context // Non-Nullable attribute

    public constructor(context : Context) {
       this.context = context
    }

    public fun doSomeVoodoo() {
       val text : String = context.getString(R.string.abc_action_bar_home_description)
    }
}
Run Code Online (Sandbox Code Playgroud)

D3x*_*ter 20

如果您在构造函数中做的唯一事情是赋值,那么您可以将Primary Constructor与私有Property一起使用.

例如:

public class TestClass(private val context: Context) {

  public fun doSomeVoodoo() {
     val text = context.getString(R.string.abc_...)
  }
}
Run Code Online (Sandbox Code Playgroud)


Jay*_*ard 19

如D3xter所示,您可以选择在构造函数中设置它.你还有其他选择.他们都在......

在构造函数中创建属性(根据@ D3xter),这是由主构造函数直接初始化的简单属性的最常见情况:

class TestClass(private val context: Context) {
    fun doSomeVoodoo() {
        val text : String = context.getString()
    } 
}
Run Code Online (Sandbox Code Playgroud)

您可以声明val属性而不是初始化它,假设所有可能的构造函数都实际初始化它(根据您提出的问题中的第二个示例). 当您有多个可以不同方式初始化值的构造函数时,这是正常的:

public class TestClass {
    private val context: Context

    public constructor(context : Context) {
        this.context = context
    }

    // alternative constructor
    public constructor(pre: PreContext) {
        this.context = pre.readContext()
    }

    public fun doSomeVoodoo() {
        val text : String = context.getString()
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以传入非属性声明的构造函数参数,然后在属性初始化中使用它们. 当您有更复杂的初始化或需要使用委托属性时,这很常见:

class TestClass(context: PreContext) {
    private val context : Context by lazy { context.readContext() }
    private val other: List<Items> = run {
        context.items.map { it.tag }.filterNotNull()
    }
    private val simpleThing = context.getSimple()

    fun doSomeVoodoo() {
        val text : String = context.getString()
    }
}
Run Code Online (Sandbox Code Playgroud)

在构造期间无法初始化值时使用lateinit修饰符,但您确定它将在您第一次读取访问之前完成. 当依赖注入,IoC容器或某些东西创建类的空版本然后立即初始化它时,这很常见:

class TestClass() {
    private lateinit var context : Context // set by something else after construction

    fun doSomeVoodoo() {
        val text : String = context.getString()
    }
}
Run Code Online (Sandbox Code Playgroud)

因为lateinit该属性当前必须是a var并且不适用于基本类型.

var如果使用为此目的设计的委托,也可以声明属性而不初始化它,例如Delegates.notNull(). 当你想要一个没有初始状态但是在未知时间点构造之后设置时,这类似于lateinit常见的var:

public class TestClass() {
    private var context: Context by Delegates.notNull()

    public fun doSomeVoodoo() {
        // if context is not set before this is called, an exception is thrown
        val text : String = context.getString()
    }
}
Run Code Online (Sandbox Code Playgroud)