首页 文章

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

提问于
浏览
14

我创建了一个带有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)
    }
}

不幸的是,我必须使用“?”将属性声明为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)
    }
}

3 回答

  • 1

    我有一个类似的问题,我不想在施工后 grab 物体 . 使用 lazylateinit 会导致字节码效率低下,所以经过一些研究后我决定使用这种方法并返回发布答案,以防它有所帮助:

    解决方案

    class TestClass(context: Context) {
        private val homeDescription: String
    
        init {
            homeDescription = context.getString(R.string.abc_action_bar_home_description)
        }
    
        fun doSomeVoodoo() {
            val text : String = homeDescription
        }
    }
    

    或者,上述内容可以进一步简化为:

    class TestClass(context: Context) {
        private val homeDescription: String = context.getString(R.string.abc_action_bar_home_description)
    
        fun doSomeVoodoo() {
            val text : String = homeDescription
        }
    }
    

    Decompiled Bytecode

    并且反编译的java版本比其他方法更容易接受,并且在构造之后不保留对上下文的引用:

    public final class TestClass {
        private final String homeDescription;
    
        public final void doSomeVoodoo() {
            String text = this.homeDescription;
        }
    
        public TestClass(@NotNull Context context) {
            Intrinsics.checkParameterIsNotNull(context, "context");
            super();
            String var10001 = context.getString(2131296256);
            Intrinsics.checkExpressionValueIsNotNull(var10001, "context.getString(R.stri…ion_bar_home_description)");
            this.homeDescription = var10001;
        }
    }
    
  • 18

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

    例如:

    public class TestClass(private val context: Context) {
    
      public fun doSomeVoodoo() {
         val text = context.getString(R.string.abc_...)
      }
    }
    
  • 17

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

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

    class TestClass(private val context: Context) {
        fun doSomeVoodoo() {
            val text : String = context.getString()
        } 
    }
    

    您可以声明 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()
        }
    }
    

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

    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()
        }
    }
    

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

    class TestClass() {
        private lateinit var context : Context // set by something else after construction
    
        fun doSomeVoodoo() {
            val text : String = context.getString()
        }
    }
    

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

    如果使用为此目的设计的委托,例如 Delegates.notNull() ,也可以声明 var 属性并且不初始化它 . 这类似于 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()
        }
    }
    

相关问题