During our last Advent of Kotlin, there was one particularly tricky moment where we had to create an instance of any class for testing purposes. This is not an easy task. Different classes are created in different ways. They might have constructors that require other objects. Let’s see how one might create a random class —an instance created using the random constructor, to which we pass random data.

We will do it step-by-step in a TDD way (Test Driven Development).

Just before we start, this article requires some experience with Kotlin. If you are new to this world, it might be tough. If you want to learn Kotlin, you can join our workshops.

Step 1: Class with an empty constructor

Class with an empty constructor is the simplest case because all we need to do is to use this constructor. What we expect is that when we have a class without any parameters on its constructor, we can create it using our function:

As you can see, makeRandomInstance needs to know about the type of A . To not have it erased, we need this type parameter to be reified:

Then we can reference class using T::class . Creating an instance from an empty constructor is easy in JVM. We can just use newInstance :

This method works, but only on JVM. To make universal implementation we need to use Kotlin reflection:

What happens in this code is that we first reference class ( T::class ), then we take constructors property which contains all constructors that this class offers. From those constructors, we take the one which has no parameters. Finally, we call this constructor, and by doing that we create an instance of T .

Step 2: More parameters

Everything gets more challenging when we have more parameters:

Also, if there are no constructors, we will throw an error.

To solve it, let’s start from finding a constructor with the smallest number of parameters and using it. Then to create each argument, we will recurrently use makeRandomInstance . Though to use it, we need to pass the argument type. To find it out, we can check what is declared as a parameter in this constructor. We take a parameter, ask for its type and then we take its classifier. If this is not a generic type, then it will be an instance of KClass that we can recursively use because this is the same as T::class . We will talk about handling generic types later. For now, we need to extract a separate function to which we are passing a type:

This works for us, but what if some constructor cannot be used while another can be?

The simplest answer is that we should iteratively search for a constructor we can use:

On end, we don’t care which one will be used since we want a random instance anyway. Later we will also randomize the order in which we try constructors.

Step 3: Primitives

There are some types that are not created via constructors and that are not supported by a function. Primitives! For simplification I included only the most important ones:

We need a separate way to create each of them. We can make random instances using Random :

It should also work for classes where primitives are passed via constructor:

Stop

This is a point where I would like to stop most of you. Our implementation is already supporting most types, and it should give you the idea. After this point, we need to enter into the world of generics and things get nastier. We need to start using hacks and platform-specific reflection. It is because generics are problematic in JVM. They were introduced in Java 5 (J2SE 5.0), and they do not really exist in the JVM bytecode. So generic types in Java are erased during compilation time. There are some tricks to reference them, but they are not beautiful nor perfect. This is why I encourage you to stop at this satisfactory solution with some understanding of what is the logic behind random class creation. If you prefer to stay, let’s dive into generics.

Step 4: Standard interfaces

There are many interfaces that we operate on without even knowing what classes are under the hood. For instance List or Map . We create them using listOf or mapOf and we don’t need to know what does it really returns as long as it is an instance of List or Map . Since they are often used, we need to support those interfaces as well:

We should also support classes that need those collections as constructor arguments:

If you are wondering why B is declared outside of the class, it is because of this error.

Let’s assume that we don’t want empty collections. That we want them with some values. There is a problem. Standard collections are generic types. To fill them, we need to generate instances that associate to their type arguments. For that, we need to know those type arguments! But types are erased in JVM. The trick for that is to take a type reference. It is not supported natively, but a solution was implemented by Alexander Udalov. Here is a link to getKType function:

Having that, we can reference type, and later use it to get types or arguments.

The limitation is that this function does not support nullable types, so we will not produce nulls at all.

Of course, this type will be now used only for collections. This is how we can add support for creating random collections with random size from 0 to 10:

Notice that we use KType to get generic types when we create a list or map, and then recursively create instances.

Step 5: Generic types

Let’s start supporting generic types as well:

The problem is that when a constructor parameter is a generic type, it is not a concrete class, but instead a type parameter:

class A<T>(t: T)

This is why when we take its classifier, it is not a KClass but instead, it is KTypeParameter . So with the previous implementation, we will have an error on:

paramType.classifier as KClass<*>

Instead, we need to add support for KTypeParameter . Type parameter does not have any information about passed type argument. It is trying to tell us what type was passed to A seeing only how A is defined. Where this information can be found? In the general type! The problem is that in the class types, you know that for instance type argument in A<String> is String , but we don’t know what is a name of the type parameter that associates to this type argument. This is why we also need to have a general class reference. Using those 3, we can use type parameter reference to get a name, use this name and class reference to tell from which position do we want to take type argument, and finally use this position to take concrete type argument and use this argument type:

Step 6: Randomization, configuration, and polishing

Let’s work on making these instances more random. Though what does it mean? Does it mean that a random list should have a random length from 0 to Int.MAX_VALUE ? What should be the length of String we produce? Those are questions that do not have a clear answer. Let’s just let user decide by making some properties configurable. We will let user configure those lengths and object that shoud be used if Any is expected:

Tests:

We should also let user set concrete Random . Thanks to that we will be able to do tests like this one:

To not pass this random and configuration as arguments, I decided to wrap all helper functions into a class. So this is the final implementation together with tests:

It might support more primitive types or collections. It might have support for arrays. More things could be configurable. Though those are details. At this point, you should know perfectly how to add all those elements. You should also have more familiarity with Kotlin reflection. It is not easy nor efficient, but as you can see, in the end, we can use it to do nearly anything.

Reflection on workshops

In Kt. Academy we conduct Kotlin workshops worldwide. Basics of reflection are included in the standard workshop about pure Kotlin or in the Kotlin for Backend developers. Though if you already use reflection in your company, just let me know and we can cover logic and classes in the Kotlin reflection.