1. Overview

When working with Kotlin, we might encounter an error that reads, “Type mismatch: inferred type is Unit but Void was expected”. This error can be confusing, especially for those transitioning from Java.

In this quick tutorial, we’ll examine this error in more detail and discuss how to resolve it.

2. Introduction to the Problem

First, let’s reproduce this error through an example:

fun customGreetingVoid(name: String, greeting: (String) -> Void) = greeting(name)

Here, we created a function with two arguments:

  • name – a regular non-null String
  • greeting – a function taking a String as an argument and returning Void

The customGreetingVoid() implementation passes name to the greeting() function. Therefore, *it allows us to customize the greeting‘s implementation and perform the dynamic “greeting” operation.

Next, let’s see how to use this function by an example:

customGreetingVoid("Tom Hanks"){
    log.info("Hi $it, how do you do?")
}

In the above code, we pass the StringTom Hanks“ as the name parameter and a lambda expression as the functional parameter. The lambda expression outputs a customized greeting message using log.info(), where log is an instance of org.slf4j.Logger.

Further, log.info()* returns *void:

public void info(String msg);

In other words, log.info() doesn’t return anything. The greeting functional parameter requires (String) -> Void. So, we think the types match, and we expect to see the output like this:

... [INFO] Hi Tom Hanks, how do you do?

However, the code doesn’t compile. When we attempt to compile it, we see the compiler error:

Kotlin: Type mismatch: inferred type is Unit but Void was expected

Some of us may consider asking the lambda to return null:

customGreetingVoid("Kai"){
    log.info("Hello $it")
    null
}

But this code doesn’t compile either since a non-null type is required:

Kotlin: Null can not be a value of a non-null type Void

Next, let’s understand what causes this compilation error and see how to fix it.

3. Understanding the Error

As mentioned earlier, our lambda expression uses a Java method, log.info(), that returns void. On the other hand, the error message explicitly mentions the mismatched types: Unit and Void.

So, let’s quickly understand what void, Void, and Unit are:

  • void – A Java keyword that indicates a method doesn’t return any value; examples include log.info() and System.out.println()
  • VoidA Java class that represents the void keyword through a static Class object; we’ll look at its source later
  • UnitA Kotlin type that represents Java’s** void keyword; it’s also a type that can define a function’s return type or be used in Kotlin reflections

Next, let’s discuss the cause of the compiler error.

First, let’s look at the functional parameter definition — greeting: (String) -> Void.

We intend to have a function that takes a String as the parameter and returns nothing. However, the return type we used is Void, which is a regular Java class:

public final class Void {
    public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");

    /*
     * The Void class cannot be instantiated.
     */
    private Void() {}
}

In other words, this parameter expects a function that returns an instance of Void, although Void is only for reflection usage and cannot be instantiated due to the private constructor.

log.info() is a Java method call in our lambda expression that returns void (nothing). We’ve learned that in Kotlin, Unit represents Java’s void keyword. Therefore, our lambda expression returns Unit.

Thus, the cause of the compiler error is clear: we passed a function (lambda expression) that returns Unit as a parameter that requires a Void instance as the return type.

4. Resolving the Error

Fixing the problem becomes a simple task since we understand the cause of the error. Changing the functional parameter’s return type from Void to Unit resolves the error:

fun customGreetingUnit(name: String, greeting: (String) -> Unit) = greeting(name)

Now, if we run the code with a similar lambda:

customGreetingUnit("Tom Cruise") {
    log.info("Hi $it, how are you doing?")
}

We see the expected output:

12:40:28.618 [main] INFO ... - Hi Tom Cruise, how are you doing?

So, the problem is solved.

5. Conclusion

In this article, we’ve discussed the compiler error, “inferred type is Unit but Void was expected” and how to fix it. This error in Kotlin arises from misunderstandings about Kotlin’s type system, particularly regarding Unit.

As always, the complete source code for the examples is available over on GitHub.