GuessWho
GuessWho

Reputation: 672

Kotlin array does not save value after initiation

Faced with unexpected problem. Here is simple class:

class PaymentPrintTest {

    init {
        prepareToPrint()
    }

    private var sale: Int? = null
    private var saleContent: ArrayList<Int> = ArrayList()

    private fun prepareToPrint() {

        sale = 5
        saleContent = arrayListOf(1,2,3)

        Log.i("WhereIsContent?", "prepare sale: $sale")
        Log.i("WhereIsContent?", "prepare saleContent: ${saleContent.size}")
    }

    fun startPrint() {

        Log.i("WhereIsContent?", "start sale: $sale")
        Log.i("WhereIsContent?", "start saleContent: ${saleContent.size}")

    }

}

This way I call method startPrint:

PaymentPrintTest().startPrint()

In logcat I see:

I/WhereIsContent?: prepare sale: 5
I/WhereIsContent?: prepare saleContent: 3

I/WhereIsContent?: start sale: 5
I/WhereIsContent?: start saleContent: 0

The question is where the content of saleContent value has gone? And why 'sale' variable has correct value at the same time?

Upvotes: 0

Views: 118

Answers (4)

Khamidjon Khamidov
Khamidjon Khamidov

Reputation: 8899

I know the answers above provide correct result. But, I just want to give a bit of an explanation.

1) lets start with

var sale: Int? = null

if init block comes before declaration of properties, init block just takes its property names not values assigned to them(even if it were var sale: Int? = 4, it would ignore 4). It assigns default values to them, in your case null, but this null is NOT the one you assigned, it is default null of nullable property. So if it was var sale: Int = 4, value of sale in init block would be 0(default value of Int). Then, you assigned 5 to sale. After init block finishes, sale property is checked. If default value has been assigned to that property(in your case null) it just ignores the assigment(leaving with its current value which is 5), otherwise it reassigns the property again(so if it were var sale: Int? = 4, it would again reassign it to 4)

But, if init comes after property declaration, everything should be clear and as expected;).

2) lets turn to

private var saleContent: ArrayList = ArrayList()

in this case, saleContent's property name is taken(not ArrayList()) and because no default value for this, nothing is assigned to it. Inside init block you assigned (1, 2, 3) which changed its value from not assigned to (1,2,3). But, when init block finished, it was reassigned to (ArrayList()) because ArrayList() is not default value of ArrayList.

but if put properties above init, it works as usual.

    private var sale1: Int? = null
    private var sale2: Int? = 3
    private var sale3: Int = 0
    private var sale4: Int = 9
    private var saleContent: ArrayList<Int>? = null
    lateinit var saleContent2: ArrayList<Int>

if you play around with these values, I hope you will have more understanding.

But generally, I would suggest putting init block after property declaration to be safe.

Upvotes: 0

deHaar
deHaar

Reputation: 18568

You can make saleContent a lateinit var, but you have to remove the direct initialization for that. See this example:

class PaymentPrintTest {

    init {
        prepareToPrint()
    }

    private var sale: Int? = null
    private lateinit var saleContent: ArrayList<Int>

    private fun prepareToPrint() {
        this.sale = 5
        this.saleContent = arrayListOf(1,2,3)

        println("WhereIsContent?\tprepare sale: $sale")
        println("WhereIsContent?\tprepare saleContent: ${saleContent.size}")
    }

    fun startPrint() {
        println("WhereIsContent?\tstart sale: $sale")
        println("WhereIsContent?\tstart saleContent: ${saleContent.size}")
    }
}

I had to use println() instead of Log because I wrote this in the Kotlin Playground without a LogCat...

However, the result of

fun main() {
    PaymentPrintTest().startPrint()
}

is

WhereIsContent? prepare sale: 5
WhereIsContent? prepare saleContent: 3
WhereIsContent? start sale: 5
WhereIsContent? start saleContent: 3

It obviously works this way, but it also does the way @Jaime answered.

Upvotes: 0

Shweta Chauhan
Shweta Chauhan

Reputation: 6981

As you initialise arraylist globally it will take global arraylist for both method.

If you change it like this it will work as you want.

    class PaymentPrintTest {

    init {
        prepareToPrint()
    }

    private var sale: Int? = null
    private var saleContent: ArrayList<Int>? = null

    private fun prepareToPrint() {

        sale = 5
        saleContent = arrayListOf(1,2,3)

        Log.i("WhereIsContent?", "prepare sale: $sale")
        Log.i("WhereIsContent?", "prepare saleContent: ${saleContent?.size}")
    }

    fun startPrint() {

        Log.i("WhereIsContent?", "start sale: $sale")
        Log.i("WhereIsContent?", "start saleContent: ${saleContent?.size}")

    }
}

output :

> I/WhereIsContent?: prepare sale: 5
I/WhereIsContent?: prepare saleContent: 3
I/WhereIsContent?: start sale: 5
I/WhereIsContent?: start saleContent: 3

Upvotes: 0

Jaime
Jaime

Reputation: 388

Move sale and saleContent above init, properties should be at the very top

Upvotes: 3

Related Questions