1. Introduction

In Kotlin, classes are fundamental building blocks of the language, and we use them to define objects and their behaviors. While it’s common to pass arguments of various data types, we sometimes need to pass a class to a function as a parameter.

Passing a class to a function allows us to use it as a parameter in the function, making it possible to perform operations on it. In this tutorial, we’ll explore passing a class reference to a function in Kotlin.

2. Using a Class Reference

In Kotlin, every class is associated with a class reference. There are two types of class references in Kotlin. The KClass is the Kotlin class reference:

MyClass::class

There’s also the traditional Class reference we’re familiar with from Java:

MyClass::class.java

2.1. Class vs. KClass

As mentioned, there are two representations for class references: Class and KClass. The main difference is that KClass provides additional Kotlin-specific features like type parameters and nullable types. What’s more, KClass provides a unified way of working with classes written in Kotlin, Java, and other JVM languages, while Class is used primarily for Java interop purposes.

2.2. Access Using a Class Parameter

First, let’s define a class we can use to pass as a parameter to a function:

class ParameterClass{

    fun parameterClassMethod(): String{
        return "This is a method called on our class"
    }
}

This is a simple class with just one method that returns a String.

We can now define our function that accepts this class as a parameter using a class reference:

fun <T: ParameterClass> functionAcceptingClassReference(clazz: Class<T>): String {
    val instance = clazz.newInstance()
    return instance.parameterClassMethod()
}

The method takes a single parameter clazz, a class reference of type T that must be a sub-class of ParameterClass.

We create an instance of the class reference using the newInstance() Class method. This creates a new instance of ParameterClass.

Finally, we call paramterClassMethod() on the instance of ParameterClass and return the result of the method call as a String.

Let’s now test this method to ensure it works properly:

@Test
fun `passes class as function parameter using class reference`(){
    val result = functionAcceptingClassReference(ParameterClass::class.java)

    assertEquals("This is a method called on our class", result)
}

In this unit test, we pass in our Class reference to ParameterClass as a parameter to our method. Finally, we assert this method returns the expected string from our ParameterClass to prove we used the type reference.

2.3. Access Using a KClass Parameter

Alternatively, we can pass a KClass parameter to our function instead of a Class parameter:

fun <T: ParameterClass> functionAcceptingKClassReference(clazz: KClass<T>): String {
    val instance = clazz.createInstance()

    return instance.paramterClassMethod()
}

This helper method accepts a KClass parameter. This parameter must be a KClass reference that extends ParameterClass

Unlike the previous approach, KClass provides createInstance() to create an instance of ParameterClass. Finally, we use this instance to invoke the parameterClassMethod():

@Test
fun `passes class as function parameter using kclass reference`(){
    val result = functionAcceptingKClassReference(ParameterClass::class)

    assertEquals("This is a method called on our class", result)
}

Finally, we assert that our helper method returns the correct string from our ParameterClass method.

3. Using a reified Type Parameter

Alternatively, we can use a reified type parameter to pass a class to a function. It allows us to access the class of a generic type parameter at runtime.

The reified keyword allows access to the generic type argument without passing it in as a parameter. What’s more, we can easily obtain the Class or KClass object of a class using the type argument:

inline fun <reified T: ParameterClass> functionAcceptingClassNameUsingReifiedParameters(): String {
    val instance = T::class.java.newInstance()
    
    return instance.parameterClassMethod()
}

Similarly, this method creates an instance of the ParameterClass class from the. class reference. We then return the string from the parameterClassMethod() method:

@Test
fun `passes class as function parameter using reified parameters`(){
    val result = functionAcceptingClassNameUsingReifiedParameters<ParameterClass>()

    assertEquals("This is a method called on our class", result)
}

In this unit test, we pass in the ParameterClass type as a parameter thanks to the reified type parameter. Finally, we assert that the functionAcceptingClassNameUsingReifiedParameters() method returns the correct string.

4. Using Java Reflection

Finally, we can use Java’s reflection to pass a class to a function. This is done by passing the fully qualified class name as a String and then using reflection to create an instance of the class:

fun <T : ParameterClass> functionAcceptingClassNameUsingReflection(className: String): String {
    val clazz = Class.forName(className) as Class<T>
    val instance = clazz.newInstance()
    
    return instance.parameterClassMethod()
}

This method first obtains a reference to the Class object corresponding to the className parameter by calling the Class.forName(className) method. The as keyword casts the Class object to the Class of the generic type T.

Next, we create a new instance of the class.

Finally, we call the parameterClassMethod() method on the instance of the ParameterClass:

@Test
fun `passes class as function parameter using reflection`(){
    val result = functionAcceptingClassNameUsingReflection<ParameterClass>("com.baeldung.classParameterToFunction.ParameterClass")

    assertEquals("This is a method called on our class", result)
}

We provide our method with the fully qualified class name as a String. This method will then access the parameterClassMethod() method from the ParameterClass class and return the desired string.

5. Conclusion

In this article, we’ve discussed different ways to pass a class to a function in Kotlin. We covered the Class reference, reflection, and reified type parameter approaches. We should evaluate which approach fits our project needs the best.
As always, the complete source code used in this article is available over on GitHub.
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments