0

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?

2
  • 1
    put init block after properties Commented May 27, 2020 at 14:13
  • Make saleContent a lateinit var and remove the initialization in that line: private lateinit var saleContent: ArrayList<Int>. The remaining code can stay as it is. Commented May 27, 2020 at 14:15

4 Answers 4

3

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

Sign up to request clarification or add additional context in comments.

1 Comment

The correct code would be nice in this answer, otherwise: (one of) the correct answer(s)
0

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

2 Comments

I think making a member nullable when it can be avoided is not the way to go... But I won't downvote this because it seems to work.
then you can use lateinit or @jaime answered
0

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.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.