Published on

Kotlin Higher Order Functions

Authors

Java 8 made it possible to pass lambdas around. This is really cool but I find it cumbersome when you need to define lambdas other than the ones in the standard function library. For example I had a case where I defined a template for a boiler plate piece of code where the handling of the error can change with different usages of the template. The standard library did not provide an error supplier so I had to roll my own out. This is not too bad as you define a functional interface but then I end up having to create more classes than I feel I need just to define the shape of a function I want as in the following:

package my.test;

@FunctionalInterface
public interface ErrorSupplier {

    void handleError(Exception exceptionToHandle);

}

I would then use this in my code for example for something like the below:

package my.test;

import org.springframework.http.ResponseEntity;

import java.util.function.Supplier;

public class RestHandlerTemplate<T> {

    public ResponseEntity processRequest(Supplier<T> requestToProcess, ErrorSupplier errorHandler) {
        try {
            return ResponseEntity.ok(requestToProcess.get());
        } catch (Exception e) {
            errorHandler.handleError(e);
            return ResponseEntity.badRequest().build();
        }
    }
}

In Kotlin this is significantly easier and less verbose:

package my.test

import org.springframework.http.ResponseEntity

class RestErrorTemplate<T> {

    fun processRequest(requestToProcess: () -> T, errorHandler: (error: Exception) -> Unit): ResponseEntity<*> {
        return try {
            ResponseEntity.ok(requestToProcess())
        } catch (e: Exception) {
            errorHandler(e)
            ResponseEntity.badRequest().build<Any>()
        }
    }
}

Much better, we defined the errorHandler in the method inline without having to make a separate functional interface for it and it is also far more readable once you wrap your head around the syntax. This decompiles to the following Java code:

package my.test;

import kotlin.Metadata;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.ResponseEntity;

@Metadata(
   mv = {1, 1, 9},
   bv = {1, 0, 2},
   k = 1,
   d1 = {"\u00004\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\u0018\u0000*\u0004\b\u0000\u0010\u00012\u00020\u0002B\u0005¢\u0006\u0002\u0010\u0003J?\u0010\u0004\u001a\u0006\u0012\u0002\b\u00030\u00052\f\u0010\u0006\u001a\b\u0012\u0004\u0012\u00028\u00000\u00072%\u0010\b\u001a!\u0012\u0017\u0012\u00150\nj\u0002`\u000b¢\u0006\f\b\f\u0012\b\b\r\u0012\u0004\b\b(\u000e\u0012\u0004\u0012\u00020\u000f0\t¨\u0006\u0010"},
   d2 = {"Lmy/test/RestErrorTemplate;", "T", "", "()V", "processRequest", "Lorg/springframework/http/ResponseEntity;", "requestToProcess", "Lkotlin/Function0;", "errorHandler", "Lkotlin/Function1;", "Ljava/lang/Exception;", "Lkotlin/Exception;", "Lkotlin/ParameterName;", "name", "error", "", "production sources for module scrape_main"}
)
public final class RestErrorTemplate {
   @NotNull
   public final ResponseEntity processRequest(@NotNull Function0 requestToProcess, @NotNull Function1 errorHandler) {
      Intrinsics.checkParameterIsNotNull(requestToProcess, "requestToProcess");
      Intrinsics.checkParameterIsNotNull(errorHandler, "errorHandler");

      ResponseEntity var10000;
      ResponseEntity var3;
      try {
         var10000 = ResponseEntity.ok(requestToProcess.invoke());
         Intrinsics.checkExpressionValueIsNotNull(var10000, "ResponseEntity.ok(requestToProcess())");
         var3 = var10000;
      } catch (Exception var5) {
         errorHandler.invoke(var5);
         var10000 = ResponseEntity.badRequest().build();
         Intrinsics.checkExpressionValueIsNotNull(var10000, "ResponseEntity.badRequest().build<Any>()");
         var3 = var10000;
      }

      return var3;
   }
}