Introduction
Whenever I want to express method parameters, return type or variables as a method in Java, I often find myself having to do a bit of work:
- If I cannot remember which pre-made functional interface to use, then I will have to look up the list of pre-made functional interfaces from the Java SE documentation.
- But the problem does not stop there. If none of the functional interfaces on the list fit my use case, then I would have to resort to making my own functional interface. This is tedious and pollute my project with functional interfaces, especially if they are only used once.
Kotlin (and a few other languages) have a feature called Function Type, which allows you to declare function parameters, function return value, and variables as having a function as their types. With function types, we can skip creating our own functional interfaces, reducing boilerplate.
In this tutorial, we will learn how to use Function Types in our code.
Goals
At the end of the tutorial, you would have learned:
- How to create and use Function Types.
Tools Required
- A Kotlin IDE such as IntelliJ IDEA version
2022.2
(Community Edition). - The Kotlin version used in this tutorial is
1.7.10
.
Prerequisite Knowledge
- Basic Kotlin.
- Java Functional Interfaces (optional).
Project Setup
To follow along with the tutorial, perform the steps below:
- Create a new Kotlin project using Gradle as the build system.
- For the Project JDK, use JDK 18.
Function Type As Function Parameter
The code below illustrates how Functional Interfaces can be used on Java method parameters.
void functionalParams(Function<Integer,Integer> timesTwo){
timesTwo.apply(1);
}
This is the Kotlin equivalence.
fun functionalParams(timesTwo: Function<Int, Int>){
timesTwo.apply(1)
}
Using Function Type, the Kotlin equivalence can be rewritten as.
fun functionTypeParams(timesTwo: (Int)->Int){
timesTwo(1)
}
As you can see, Function<Int,Int>
has been replaced with (Int)->Int
, which has the same parameter type and return type as Function<Int,Int>
’s SAM (single abstract method) Integer apply(Integer t)
.
In my opinion, (Int)->Int
is easier to read than Function<Int,Int>
as well because the SAM is completely exposed as you are reading the code. When encountering Java’s functional interfaces, I often find myself looking up the method signature of the SAM as a sanity check.
As an added bonus, we were able to skip the apply()/invoke()
function call as well because of the convenient invoke()
operator.
Function Type is not an exclusive to Kotlin, other languages that I am familiar with also have a similar feature. TypeScript and Swift versions of functionTypeParams()
are shown below.
TypeScript:
function functionTypeParams(timesTwo: (n: number) => number) {
timesTwo(1)
}
Swift:
func functionTypeParams(_ timesTwo: (Int)->Int) {
timesTwo(1)
}
Function Type As Function Return Type
Function Types can also appear as a function’s return type. The code below shows how this looks like.
fun returnFunctionType(): (Int)->Int = { n -> n * 2 }
Kotlin allows us to skip explicit type declaration for return types as well, so we can have the return type inferred by the compiler, producing a shorter version below.
fun returnFunctionTypeAlt() = { n: Int -> n * 2 }
Function Type As Variable Type
Lastly, you can also assign Function Types to variables.
val timesTwo: (Int)->Int = { n -> n * 2 }
timesTwo(1)
Similarly to how we were able to skip the explicit type declaration, we can also skip the explicit type declaration on variables.
val timesThree = { n: Int -> n*3 }
timesThree(1)
Higher-Order Functions
A function is also considered a Higher-Order Function when it either receives a function as a parameter or returning a function.