Reputation: 1340
I have a Building
class that represents an array of Room
objects. Each room has some properties which are irrelevant for now.
I am getting the rooms from user input like this:
"[room index] [room properties...]"
. The index of each room ranges from 0 to n -1.Example input:
3
0 [room 0 properties: a, b]
2 [room 2 properties: x, y]
1 [room 1 properties: u, v]
which should create the object:
Building: rooms: [Room(a, b), Room(u, v), Room(x, y)]
I can't guarantee that the rooms will be given in order, but I can promise that all rooms will be given as input. If I was in Java, I could easily write code like this:
Scanner in = new Scanner(System.in);
Room[] rooms = new Room[in.nextInt()];
for (int i = 0; i < rooms.length; i++) {
int roomIndex = in.nextInt();
// Get room properties from rest of line input
rooms[roomIndex] = new Room(...);
}
However, in kotlin, there is a little problem with this approach: I have to initialize the rooms
array when I create it. I can't create an uninitialized array, like in Java, and then initialize it in a loop. The Array
constructor in kotlin takes a size and a lambda parameter to initialize the array in the order of the indices. As I said, I can't promise the input will be given in the order of indices. I can only promise that all rooms will be given and initialized.
I can create an array of nullable rooms (Array<Room?>
), but I don't like this idea because all rooms will be given, so null checks and assertions will be useless when using the array later.
Is there a way to create an uninitialized array in kotlin, and tell the compiler not to freak out about this because I promise the array will be fully initialized after the loop runs?
I didn't find a way to cast an Array<Room?>
to Array<Room>
without creating a copy of the array, which seems wasteful for me. I also don't think I can use something like Array<lateinit Room>
as I can do with regular non-collection properties. The only solution I came up with is to initialize the array with a dummy Room
object and then actually initialize it in the loop:
val dummy = Room(...) // dummy object with useless properties
val rooms = Array(size) { dummy }
// Real initialization in a loop
repeat(rooms.size) {
val properties = readline()!!.split(' ')
val roomIndex = properties[0].toInt()
// Parse rest of properties
rooms[roomIndex] = Room(...)
}
What is the best way to create such an array? I don't think I should use Array<Room?>
because I can promise every element of the array will be non-null after the loop, and I don't like the idea of a dummy object, but it might be the best bet? Do you have another suggestion?
Thanks in advance.
Upvotes: 1
Views: 212
Reputation: 7882
I didn't find a way to cast an Array<Room?> to Array without creating a copy of the array
Casting doesn't create a copy of the array (or any other object one cast). It just creates new variable with different type, but the same reference (see proof).
Also, you may create an auxilary function, wrapping nullable array creation and casting:
@Suppress("UNCHECKED_CAST")
inline fun <reified T> buildArray(size: Int, builderAction: Array<T?>.() -> Unit): Array<T> =
arrayOfNulls<T>(size).also(builderAction) as Array<T>
Usage:
val rooms = buildArray<Room>(size) {
val properties = readLine()!!.split(' ')
val roomIndex = properties[0].toInt()
set(roomIndex, Room(...))
}
Upvotes: 2