Code Review Videos > Kotlin > Exploring Kotlin Timer timerTask

Exploring Kotlin Timer timerTask

As part of my post on Kotlin Task Scheduling I had a piece of code that used Kotlin’s timerTask function that I wanted to dive deeper into, but that wasn’t the best place to do so. So, this post is going to cover that off.

Here’s the code for reference:

Timer()
    .schedule(timerTask {
        println("Hello from StdLib Timer: ${Date.from(Instant.now())}")
    }, 0L, 5000L)
Code language: Kotlin (kotlin)

And to quickly recap at a very high level:

  • Timer() creates a new instance of the Timer class, which is used to schedule tasks to be executed at a later time.
  • .schedule() is a method on the Timer class that schedules a task to be executed after a certain delay and at a certain interval.
  • timerTask { ... } creates a new instance of a TimerTask, which is the code that will be executed by the Timer.
  • println("Hello from StdLib Timer: ${Date.from(Instant.now())}") is the code that will be executed by the TimerTask. In this case, it simply prints a message to the console that includes the current date and time.
  • 0L is the initial delay before the TimerTask is executed for the first time. In this case, the delay is zero, meaning the TimerTask will be executed immediately.
  • 5000L is the interval at which the TimerTask will be executed repeatedly. In this case, the interval is 5000 milliseconds, or 5 seconds. This means that the TimerTask will be executed every 5 seconds until it is cancelled.

OK, so code covered.

But the most interesting and unusual aspect, as far as I am concerned, is where timerTask { ... } comes from. How is it named? The syntax is unusual. It looks intuitive at a glance, but when I give it more time I’m left with more questions than answers.

And that’s the purpose of this post. Diving into how that works.

Why do we need an instance of TimerTask?

For the moment, let’s not concern ourselves with where timerTask comes from inside the .schedule(...) invocation.

timer.scheduler method call

What is implicitly important about the .schedule call here is that it requires a TimerTask as its first argument.

The exclamation mark there means this argument is mandatory.

Where do these four possible variations come from?

They come from the Timer class that we instantiated on line 1 in the code sample at the very start of this post.

You can see them all on GitHub, or by clicking the method name in IntelliJ like I do for the screenshot a little further below.

    public void schedule(TimerTask task, long delay) { ...

    public void schedule(TimerTask task, Date time) { ...

    public void schedule(TimerTask task, long delay, long period) { ...

    public void schedule(TimerTask task, Date firstTime, long period) { ...Code language: Java (java)

Coming from languages like JavaScript, TypeScript, or PHP that do not allow method overloading, this may look a bit unusual. Four methods that all have the same method name, how is that possible?

Well, so long as the function arguments are different, Java is able to figure out which variation of the function you wanted to call. Pretty neat. And something that once you’ve had access too, you will miss in languages that don’t have it.

Basically it solves the problem of coming up with different function names for the same concept.

Anyway, that’s not hugely relevant to us here. Just interesting in and of itself.

The method we will use is the third one:

Java Timer methods

So somehow, we need timerTask { ... } to return an instance of TimerTask.

How does `timerTask { }` create a new instance of TimerTask?

There’s a heck of a lot happening here.

Timer()
    .schedule(
        timerTask { ... }, 
    ...
    )Code language: Kotlin (kotlin)

But the initial aha moment, for me, was that timerTask is just a function.

This is far more obvious if you use a more verbose variation of the syntax:

    timer.schedule(
        timerTask({
            println("Hello from StdLib Timer: ${Date.from(Instant.now())}")
        }), 
        0L, 
        5000L
    )Code language: Kotlin (kotlin)

IntelliJ and Kotlin best practice do not approve of this approach, however:

kotlin lambda should be moved out of parens

Kotlin’s trailing lambda syntax allows us to omit the parentheses (( and )) if the last (or only) parameter of our function is a function.

For me, this was one of those concepts I’d read about in the docs and in books, but when I saw it for the first time in “real code”, it confused me.

Exploring Trailing Lambdas

We can write our own version of a trailing lambda that is far more basic, and it should make the concept a little clearer.

But in order to see the benefit of using trailing lambas, let’s go through the other variations of ‘solving’ this problem, starting with the more verbose and aiming to become more terse or concise.

package org.example

fun ourCustomExample(functionWePassInAsAnArgument: () -> Unit) {
    println("before the passed in function is called")
    functionWePassInAsAnArgument();
    println("after the passed in function is called")
}

fun theFunctionWePassIn() {
    println("our passed in function")
}

fun main() {
    ourCustomExample(::theFunctionWePassIn)
}Code language: Kotlin (kotlin)

This is the longest form of writing this that I can think of.

First we define a function called ourCustomExample that takes in another function as an argument.

Any function that takes in a function as an argument, returns a function as output, or both, is a higher order function.

In this specific case, the argument function must take no input parameters and return no output. We provide a type for the exact ‘shape’ of the function we allow:

() -> Unit

This tells the Kotlin compiler that we expect to be given a function that takes no arguments – the empty parentheses – and return nothing, which in Kotlin is described as Unit.

The implementation for ourCustomExample is simple enough:

  1. It prints the message “before the passed in function is called”.
  2. It calls the function passed in as an argument using the syntax functionWePassInAsAnArgument().
  3. It prints the message “after the passed in function is called”.

Next, we define theFunctionWePassIn which is pretty self explanatory.

And finally in the main function we have:

ourCustomExample(::theFunctionWePassIn)Code language: Kotlin (kotlin)

Again, slightly odd syntax if new to Kotlin.

Function Reference Syntax (::)

You may be wondering why we need the ::, and that’s a good question.

What happens if we don’t use that syntax?

kotlin Type mismatch.
Required:
() → Unit
Found:
Unit

Somewhat confusingly, as least superficially, is that if we actually do what we are being told – we invoke the function – the compiler is still not happy:

kotlin type mismatch on invoked function

The issue remains, even though we called the function on line 18.

Why is that?

Remember that we explicitly said that the argument ourCustomExample should receive is a function of type () -> Unit:

fun ourCustomExample(functionWePassInAsAnArgument: () -> Unit) {Code language: Kotlin (kotlin)

If we explicitly invoke theFunctionWePassIn and pass in the result, the result is not a function. It is the outcome of that function. And there is no meaningful return value from that function.

Which in Kotlin we can express as the type of Unit.

As a side note there is another similar, but different type called Nothing, which we can use to indicate the rarer case where a function will never return normally:

fun throwException() : Nothing {
    throw IllegalArgumentException("Some invalid argument")
}Code language: Kotlin (kotlin)

So it does make sense.

We could actually re-write theFunctionWePassIn to satisfy the compiler though:

fun theFunctionWePassIn(): () -> Unit {
    return fun() { println("our passed in function") }
}

fun main() {
    ourCustomExample(theFunctionWePassIn())
}Code language: Kotlin (kotlin)

And that works because now theFunctionWePassIn returns an anonymous function that, when called, will do our println. A function that returns a function… hey, another higher order function.

But that’s quite verbose, isn’t it?

Well, yes. We could make it shorter using an expression body:

fun theFunctionWePassIn(): () -> Unit {
    return fun() = println("our passed in function")
}

fun main() {
    ourCustomExample(theFunctionWePassIn())
}Code language: Kotlin (kotlin)

Still works, same output:

before the passed in function is called
our passed in function
after the passed in function is called

Process finished with exit code 0Code language: JavaScript (javascript)

Even so, it’s not what we originally wanted.

How can we get back to the original requirement?

fun theFunctionWePassIn() {
   println("our passed in function")
}

fun main() {
    ourCustomExample(theFunctionWePassIn)
}Code language: Kotlin (kotlin)

What we want to do here is to pass the function itself, not the result of calling the function. We want ourCustomExample to handle calling that function, whenever it needs too.

The :: syntax allows us to get a reference to the function.

A function reference is a way to refer to a function in Kotlin without invoking it. It allows you to pass a function as an argument to another function or to store a reference to a function in a variable.

And that sounds like another useful part of our definition of higher order functions.

fun theFunctionWePassIn() {
   println("our passed in function")
}

fun main() {
    ourCustomExample(::theFunctionWePassIn)
}Code language: Kotlin (kotlin)

Awesome. Still works. Looks neat. Problem solved.

Right?

Well… yes. From a certain point of view, and depending on your requirements, we could call it done at this point.

But equally we could keep refactoring and see how this code could be improved and more closely follow the typical conventions, best practices, and language features that are found in Kotlin.

Storing The Function In A Variable

Closely related to the function reference approach, but solving the problem using a different perspective, we could store our function inside a variable, and then pass that variable to ourCustomExample:

val theFunctionWePassIn = fun() {
   println("our passed in function")
}

fun main() {
    ourCustomExample(theFunctionWePassIn)
}Code language: Kotlin (kotlin)

It’s almost subtly different. But different it is.

Now we have a variable named theFunctionWePassIn that contains an anonymous function. This feels more JavaScript-y to me.

I would potentially opt for this approach if the function was going to be small, simple, and used once.

I would opt for the function reference syntax over storing the function in a variable in all other cases.

Personally I find the named function approach to be more easy to think about. But your mileage may vary, of course.

Using A Lambda Expression

We don’t actually need to explicitly define the function we will pass in as its own construct.

We don’t need fun theFunctionWePassIn() { ... } and we don’t need val theFunctionWePassIn = fun() { ... }.

We can do away with these entirely, and simply pass the function literal to the ourCustomExample call:

fun main() {
    ourCustomExample({ println("our passed in function") })
}Code language: Kotlin (kotlin)

However, if we write the lambda in this way, IntelliJ is going to prompt us to refactor the code:

kotlin lambda with parentheses

Note that this still works. It is still valid code. It is simply not idiomatic Kotlin.

We’re almost at the trailing lambda syntax here.

Remember, our function – our lambda – is the only (and therefore last) argument that ourCustomExample takes, so it is possible that it can be a trailing lambda.

We can improve this, however. And before we do what IntelliJ suggests, let’s see the intermediate step.

Using Trailing Lambda Syntax (With Parens!)

The next step from the previous code example is to switch from a lambda expression to a trailing lambda.

We’ve seen some subtle differences already, and this one is another subtlety:

fun main() {
    ourCustomExample() { println("our passed in function") }
}Code language: JavaScript (javascript)

The difference here is that because the lambda is the last (only!) argument to ourCustomExample, it can (and has) been moved outside of the parentheses.

To the uninitiated, this would likely read as though it is the method body of ourCustomExample. That’s how this used to read to me, when I first started dabbling with Kotlin.

But at this point, me and you know better, don’t we?

Yes, we do.

However, there’s still an improvement to be made here. And it might not be immediately obvious, at a glance, what that is. IntelliJ’s syntax highlighting does make it more obvious, sort of:

Can you see it?

Let me give you a pointer if not.

It’s the greyed out parentheses after ourCustomExample:

kotlin Unnecessary parentheses in function call with lambda

Which does take us to the final way to write out this function.

Using Trailing Lambda Syntax (Without Parens!)

And so, after all that, here we are:

fun main() {
    ourCustomExample { println("our passed in function") }
}Code language: Kotlin (kotlin)

Hopefully that makes a lot more sense now.

It’s a strange mix of writing less code to achieve the same goal, which to Kotlin developers makes intuitive sense. But to Kotlin newbies, it is harder to understand and looks … weird, right?

So you have to go slow before you can go fast.

Back To Timer Task

After that detour through trailing lambdas, let’s recap the original code snippet that started this all off:

Timer()
    .schedule(timerTask {
        println("Hello from StdLib Timer: ${Date.from(Instant.now())}")
    }, 0L, 5000L)Code language: Kotlin (kotlin)

And if we cut out the noise:

Timer()
    .schedule(
        timerTask { ... }, 
    ...
    )

Then what we can see, or even just deduce at this point, is that timerTask is a function that takes one argument. That argument is a function, and therefore that function can be written as a trailing lambda:

kotlin timertask function

Note that I got to this timerTask function definition the same way you get to any function definition inside a JetBrains IDE – by ctrl + left clicking the function name.

And wow, isn’t there a ton more to cover here?

So many new things.

Why Twice Inlined?

There are many questions I have when I look at this function definition. But if we start from the annotation on line 145, we see:

@kotlin.internal.InlineOnlyCode language: Kotlin (kotlin)

And then immediately after on line 146 we see:

public <strong>inline</strong> fun timerTaskCode language: Kotlin (kotlin)

The emphasis is mine.

Why would we ever need an annotation and the inline modifier on the function? Surely if we have the function modifier itself, that should be enough?

This was something that wasn’t particularly easy to understand even after a Google.

The best I found was this Stack Overflow post:

stackoverflow What is the @InlineOnly annotation?

Which doesn’t directly answer the question I had.

Instead I went to Chat GPT for this one:

The @kotlin.internal.InlineOnly annotation is used in addition to the public inline modifier to enforce the inlining behavior of the function.

The public inline modifier specifies that the function should be inlined at the call site. Inlining means that the compiler replaces the function call with the actual body of the function, eliminating the overhead of a function call.

However, the inline modifier alone does not guarantee that the function will always be inlined. There are certain cases where the compiler may choose not to inline the function, such as when the function is too large or when it is called from a context where inlining is not allowed.

The @kotlin.internal.InlineOnly annotation is used to enforce the inlining behavior. It tells the compiler that the function should always be inlined, regardless of its size or context. This annotation is usually used for internal compiler purposes, and it is not intended for regular usage in application code.

By combining the public inline modifier with the @kotlin.internal.InlineOnly annotation, the code explicitly instructs the compiler to inline the function and ensures that it is always inlined, regardless of any other factors that might prevent inlining.

Chat GPT: why is the annotation @kotlin.internal.InlineOnly needed when the function is already public inline ?

I like that, as it also explains why we might use inline in the first place.

What about crossinline?

Another new one on me.

Fortunately the official Kotlin docs provide an answer on this.

And as best I can figure out, it is all about controlling the flow of execution when using higher order functions. Quite an interesting concept, and likely one that this solves quite neatly. But also not a problem that I can ever think I have directly encountered.

Here’s the best example I can come up with to explain the use case of crossinline.

In our earlier example code we had the following:

fun ourCustomExample(functionWePassInAsAnArgument: () -> Unit) {
    println("before the passed in function is called")
    functionWePassInAsAnArgument();
    println("after the passed in function is called")
}

fun main() {
    ourCustomExample { println("our passed in function") }
}Code language: Kotlin (kotlin)

All is fine here.

We execute this code and see the following:

before the passed in function is called
our passed in function
after the passed in function is called

Process finished with exit code 0Code language: JavaScript (javascript)

Now, let’s say that in some cases, we want to return early from the lambda.

In those cases it would be required that we see:

before the passed in function is called
our passed in function

Process finished with exit code 0Code language: JavaScript (javascript)

Which is to say that we should never see after the passed in function is called.

How can we do that?

kotlin 'return' is not allowed here

We cannot just whack in a return.

Why not?

Because Kotlin is deliberately stopping us from accidentally returning too early. I think.

We can use the return@ourCustomExample to return in the lambda, but not impact the calling function in any way.

fun main() {
    ourCustomExample {
        println("our passed in function");
        return@ourCustomExample;
    }
}Code language: Kotlin (kotlin)

That works, but it doesn’t stop the output of the “after” message. That doesn’t meet our requirements, but it does satisfy the compiler.

What we can do is mark the ourCustomExample function as being inline:

inline fun ourCustomExample(functionWePassInAsAnArgument: () -> Unit) {
    println("before the passed in function is called")
    functionWePassInAsAnArgument();
    println("after the passed in function is called")
}

fun main() {
    ourCustomExample {
        println("our passed in function");
        return;
    }
}
Code language: Kotlin (kotlin)

And that code now does do what we want:

before the passed in function is called
our passed in function

Process finished with exit code 0Code language: Kotlin (kotlin)

Which is great.

But what if we definitely didn’t ever want that to happen?

Which is to say that what if we gave control over to some random caller of our code, and we absolutely definitely always wanted to run println("after the passed in function is called"), regardless of what they did in their lambda?

That seems to be the point of crossinline.

We get to control the result, without us having to write that code.

kotlin return from lambda with crossinline

Which is to say that we get all the benefits of being able to inline our code, whilst retaining control of the call site potentially misbehaving.

Again – I think!

That’s my best present understanding of why and when I might want to use crossinline.

If I am wrong, please do shout up in the comments below.

Anyway, back to the timerTask!

Lambda With Receiver

If we focus now briefly on the code:

action: TimerTask.() -> UnitCode language: Kotlin (kotlin)

I say briefly as I feel lambda with receiver is complex enough to necessitate its own separate blog post. I will come on to that at some point in the future.

As best I understand it, a lambda with receiver is a special type of lambda that is ‘tied’ to a specific object. By using a lambda with receiver we can access the functions and properties of the object from the lambda by using the this keyword.

So here, when we provide a lambda as our action, inside that lambda the this keyword will refer to the instance of TimerTask.

And as we have already covered, the type of () -> Unit means this action function will take no parameters and return Unit, or no return value.

Function Return Type

A fairly easy one next, made more complex than it needs to be by the short hand function expression syntax in use.

public inline fun timerTask(crossinline action: TimerTask.() -> Unit): TimerTaskCode language: Kotlin (kotlin)

If we strip away the noise, we can see that at the very end we have :TimerTask

fun timerTask(...): TimerTask

Which states that this function returns a TimerTask object.

Function Expression

Because the timerTask function is a short hand function, or a function expression, we next have the expression itself.

Again, let’s cut out the noise and show only the function expression code:

fun timerTask() = object : TimerTask() {
    override fun run() = action()
}Code language: Kotlin (kotlin)

The peculiarity to this syntax is that it’s a function expression with what appears to be a method body.

However, what we have here is an anonymous object that inherits from TimerTask.

TimerTask is called, using the standard TimerTask() syntax, and its constructor is therefore invoked.

The curious part is that the block – the ‘stuff’ between the brackets – is the definition of the anonymous object.

And this definition overrides the run function from TimerTask and specifies that the action lambda should be called instead.

Wrapping Up

That is a heck of a lot of functionality encompassed in four lines of code.

I’m still not absolutely convinced I’ve fully understood it yet, either.

Pretty wild.

But also incredibly powerful, no?

I think that’s why I’m enjoying Kotlin so much. It has opened up a whole new bunch of doors to me, beyond what I have learned about in PHP, then JavaScript, then TypeScript.

It’s like C# in its scope and complexity, but with all the goodies of Java under the hood.

Fascinating, honestly.

I’m definitely going to do a follow up to this about lambda with receiver, as that is yet another Pandora’s box of intrigue for me. Hopefully you found something of interest in this post, and if I have made any mistakes do please leave a comment and correct me.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.