Kotlin: Initialize class attribute in constructor

前端 未结 3 652
抹茶落季
抹茶落季 2021-02-12 11:15

I create a Kotlin-class with a class attribute, which I want to initialize in the constructor:

public class TestClass {

    private var context : Context? = nul         


        
相关标签:
3条回答
  • 2021-02-12 11:47

    If the only thing you are doing in the constructor is an assignment, then you could use the Primary Constructor with a private Property.

    e.g:

    public class TestClass(private val context: Context) {
    
      public fun doSomeVoodoo() {
         val text = context.getString(R.string.abc_...)
      }
    }
    
    0 讨论(0)
  • 2021-02-12 11:51

    As shown by D3xter you have the option of setting it in the constructor. You also have other options. Here they all are...

    Create the property within the constructor (as per @D3xter), this is the most common case for simple properties initialized directly by the primary constructor:

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

    You can declare the val property and not initialize it, assuming all possible constructors do actually initialize it (as per your second example in the question being asked). This is normal when you have more than one constructor that could initialize a value differently:

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

    You can pass in constructor parameters that are not property declarations, and then use those within property initializations. This is common when you have more complex initializations or need to use delegated properties:

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

    Using lateinit modifier when you cannot initialize the value during construction, but you are sure it will be done before your first read access. This is common when a dependency injection, IoC container, or something creates an empty version of your class and then initializes it immediately:

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

    For lateinit the property must currently be a var and does not work with primitive types.

    You can also declare a var property and not initialize it if you use a delegate designed for that purpose, such as Delegates.notNull(). This is similar to lateinit and common when you want a var that has no initial state, but is set later after construction at unknown point in time:

    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()
        }
    }
    
    0 讨论(0)
  • 2021-02-12 12:04

    I had a similar problem where I didn't want to hold onto the object after construction. Using lazy or lateinit resulted in inefficient bytecode so after some research I settled on this approach and returned to post the answer in case it helps:

    Solution

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

    alternatively, the above can be further simplified into:

    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

    And the decompiled java version of this feels a bit more acceptable than the other approaches and no reference to the context is held after construction:

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题