Create an object of random class in kotlin

Viewed 172

I learned java and python in high school and I became very comfortable with python. I have recently started to learn kotlin, mainly for fun (the keyword for defining a function is fun so it has to be a fun language, right), but I have a little problem.

Let's suppose I have a hierarchy of classes for Chess pieces:

abstract class Piece {
    ...
}

class Rook : Piece() {
    ...
}

class Bishop : Piece() {
    ...
}
.
.
.

I am taking input from the user to generate the board, so if the user types r, I need to create a Rook object, if he types b, I need to create a Bishop etc.

In python, I'd probably use a dictionary that maps the input string to the corresponding class, so I can create an object of the correct type:

class Piece:
    ...


class Rook(Piece):
    ...


class Bishop(Piece):
    ...
.
.
.


input_map = {
    'r': Rook,
    'b': Bishop,
    ...
}

s = input_map[input()]()  # use user input as key and create a piece of the correct type

I was really amazed by this pattern when I discovered it. In java, I had to use a switch case or a bunch of if else if to achieve the same result, which is not the end of the world, especially if I abstract it into a separate function, but it's not as nice as the python approach.

I want to do the same thing in kotlin, and I was wondering if there is a similar pattern for kotlin since it's a modern language like python (I know, I know, python isn't new, but I think it's very modern). I tried to look online, but it seems like I can't store a class (class, not an object) in a variable or a map like I can in python.

Am I wrong about it? Can I use a similar pattern in kotlin or do I have to fall back to the when statement (or expression)?

If I am not mistaken, a similar pattern could be achieved in java using reflection. I never got to learn reflection in java deeply, but I know it's a way to use classes dynamically, what I can do for free in python. I also heard that in java, reflection should be used as a last resort because it's inefficient and it's considered "black magic" if you understand my meaning. Does it mean that I need to use reflection to achieve that result in kotlin? And if so, is it recommended to use reflection in kotlin, and is it efficient?

I'd like to know how I can approach this problem, and I accept multiple answers and additional solutions I didn't come up with. Thanks in advance.

2 Answers

This can be done without reflection.

You can map the input characters to the constructors:

val pieceConstructorsByKeyChar = mapOf(
    'r' to ::Rook,
    'b' to ::Bishop,
    // etc.
)

Getting values from a map gives you a nullable, since it's possible the key you supply isn't in the map. Maybe this is fine, if when you use this you might be passing a character the player typed that might not be supported. Then you would probably handle null by telling the player to try again:

val piece: Piece? = pieceConstructorsByKeyChar[keyPressed]?.invoke()

Or if you do the look-up after you've already checked that it's a valid key-stroke, you can use !! safely:

val piece: Piece = pieceConstructorsByKeyChar[keyPressed]!!()

Yes you can use similiar approach with Kotlin. Kotlin has many features and supports reflection. Let me write an example about your problem.

Firstly create your classes that will be generate by user input.

abstract class Piece

class Rook : Piece()
class Bishop : Piece()

Create your class map

val inputMap = mapOf(
        "r" to Rook::class.java,
        "b" to Bishop::class.java
)

Create an instance what you want using newInstance function. If your input map doesn't contains key you gave then it will return null.

    val rook = inputMap["r"]?.newInstance()
    val bishop = inputMap["b"]?.newInstance()
    
    // null
    val king = inputMap["k"]?.newInstance()

Also you can write your custom extensions to create new objects.

    fun <T> Map<String, Class<out T>>.newInstance(key: String) = this[key]?.newInstance()

    // Create an instance with extension function
    inputMap.newInstance("r")
Related