aksh1618
aksh1618

Reputation: 2571

Kotlin array of properties with custom getters

Say I have this:

class NumWithSuccessor {
    var num = 1
    val successor
        get() = num + 1
}

Now, if I want an array of nums instead,

class NumsWithSuccessors {
    var nums = Array<Int>(3){ 1 }
    val successor
        get() = /* What? */
}

My first guess is to use

get() = { Array<Int>(3){ nums[it] + 1 } }

But that would lead to creation of a new array every time I need to access a successor. Is there a simple, better way?

Practical Example:

// Need to go from this...
private val _dayWiseEventsList =             // For internal use
        MediatorLiveData<List<Event>>()

val dayWiseEventsList: LiveData<List<Event>> // For immutable, external observation
        get() = _dayWiseEventsList


// ... to this
private val _dayWiseEventsListArray =        // For internal use
        Array<MediatorLiveData<List<Event>>>(DAYS) { MediatorLiveData() }

val dayWiseEventsListArray                   // For immutable, external observation
        // Need an alternative for this
        get() = Array<LiveData<List<Event>>>(DAYS) { _dayWiseEventsListArray[it] }

Upvotes: 0

Views: 1419

Answers (4)

m0skit0
m0skit0

Reputation: 25874

You can lazily initialize it:

val successor by lazy { Array<Int>(3){ nums[it] + 1 } } 

This will create the array only once when it is first accessed, then it will return the same array on subsequent accesses.

However note that this will not update successor in case you modify nums. If you want to update it you will need to set a customer setter for nums and update successor accordingly.


Like I explained in the comments, I would make all this immutable, for example:

data class NumsWithSuccessors(val nums: List<Int> = List(3){ 1 }) {
    val successor by lazy { nums.map { it + 1 } }
}

Upvotes: 1

Sam
Sam

Reputation: 10006

class NumsWithSuccessors {
    var nums: List<Int> = List(3){ 1 }
    val successors: List<Int> = object: AbstractList<Int>() {
        override val size get() = nums.size
        override fun get(index: Int) = nums[index] + 1;
    }
}

In the above example, successors is a virtual List that doesn't actually contain data. When you try to read an item from successors, it simply looks at the underlying nums list to calculate the value. The AbstractList class deals with providing implementations for all the other list behaviour, based on the get and size implementations you supply.

This works with a List, but not with an Array. An array can't have custom behaviour; it's a primitive data type that just stores values. List is an interface, the underlying behaviour of which can be modified at will.

Upvotes: 1

Sergio
Sergio

Reputation: 30755

successor can't depend on nums, has val modifier and use cached array at the same time. The solution is to create a new list using map function every time successor property is accessed:

class NumWithSuccessor {
    var nums = Array<Int>(3){ 1 }
    val successor
        get() = nums.map { it + 1 }
}

Upvotes: 0

Mamykin Andrey
Mamykin Andrey

Reputation: 1352

You can create a setter for nums, and set successor value inside it using map operator. It prevents from creating a new array every time:

class NumsWithSuccessors {
    var successor: Array<Int> = arrayOf()
    var nums: Array<Int> = Array(3) { 1 }
    set(value) {
        field = value; successor = value.map { it + 1 }.toTypedArray()
    }

Upvotes: 0

Related Questions