Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin: Initialize class attribute in constructor

Tags:

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

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

Unfortunately I have to declare the attribute as Nullable with the "?" sign, although the attribute will be initialized in the constructor. Declaring this attribute as Nullable-attribute makes it always necessary to force an NonNull-value with "!!" or to provide a Null-check with "?".

Is there any way to avoid this, if the class attribute will be initialized in the constructor? I would like to appreciate a solution like this one:

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)     } } 
like image 701
Christopher Avatar asked Jun 25 '15 06:06

Christopher


People also ask

How do I initialize a class in Kotlin?

The primary constructor is a part of the class header, and it goes after the class name and optional type parameters. The primary constructor cannot contain any code. Initialization code can be placed in initializer blocks prefixed with the init keyword.

How does Kotlin pass value in constructor?

The block of code surrounded by parentheses is the primary constructor: (val firstName: String, var age: Int) . The constructor declared two properties: firstName (read-only property as it's declared using keyword val ) and age (read-write property as it is declared with keyword var ).

How does Kotlin initialize primary constructor?

The primary constructor is initialized in the class header, goes after the class name, using the constructor keyword. The parameters are optional in the primary constructor. The constructor keyword can be omitted if there is no annotations or access modifiers specified.


2 Answers

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()     } } 
like image 98
3 revs Avatar answered Sep 20 '22 04:09

3 revs


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_...)   } } 
like image 20
D3xter Avatar answered Sep 23 '22 04:09

D3xter