|The Programmers Guide To Kotlin: Advanced Functions|
|Written by Mike James|
|Monday, 11 September 2017|
Page 1 of 3
Although we have had a brief look at functions in an earlier chapter, functions are so central to what makes Kotlin special they deserve a chapter to themselves. In this chapter we look at how functions make Kotlin more powerful and easier to use.
Programmer's Guide To Kotlin
Now Available as a Print Book
You can buy it from: Amazon
Some Chapters Already Available On The Web
In languages such as Java, functions don't really exist outside of objects.
In fact you could say that in Java there are no functions, only methods. Of course recently this has started to change and Java now has lambda functions which can be regarded as functions that don't belong to an object.
Kotlin is much freer than Java in the way you can use functions and it has lots of features that are only now making their way to newer versions of Java. Even so, Kotlin manages to map these more advanced features back onto the earlier versions of Java and this causes some compromises.
Although we have covered some of the basic facts of functions in Chapter 2 it is worth gathering things together and presenting a more complete picture in this chapter. In the next chapter we look at variations on Kotlin's basic functions – anonymous, lambda and inline functions are covered in the next chapter.
It is probably true to say that it is Kotlin's rich and varied modifications and additions to the basic idea of a function that makes it so attractive. However, many of the features are almost ad-hoc adaptions to fit in with the way that functions are used. This can make it seem difficult to see the overall logic. In addition, code that takes full advantage of the tersest mode of expression using functions can become very difficult to understand. This is particularly true when we meet expression functions in the next chapter.
Functions & Methods
Kotlin supports both functions and functions as methods.
To define a function you use:
You can also specify a return type after the closing parenthesis. If you don't specify a return type Unit is assumed. A function with a body and a return type has to have a return statement.
If the body only has a single expression, the function can be written more simply as:
Again the return type is optional if it can be inferred.
The only surface difference between a function and a method is that a method definition is associated with a class and, when used, with an instance of that class. A function, on the other hand, isn't associated with a class or an instance of a class.
Functions can be defined at the package level i.e. not within a class, or they can be defined within other functions including within methods.
Of course all of this is a sleight of hand. To remain compatible with Java, top level functions are implemented in a static class with a name packagename.filename where filename is the name of the file including the Kt extension.
So, if the hello world was stored in a file called Hello.kt, the class is demo.HelloKt, assuming the package is called demo.
This means that the first thing that a new Kotlin programmer encounters is a top level function called main with no sign of the customary static class so familiar in Java. This is just syntactic sugar as the main function is indeed a member of a static class with the same name as the package and file. You can change the name of the static class using an annotation, see Chapter 13:
As a consequence of this implementation method you can see that top level functions and methods are the same thing under the skin, and any feature you can use with one you can use with the other.
Local functions, however, are a little different. They are not accessible outside of the containing function, and they have access to the containing function’s variables but it doesn't have access to theirs. This is exactly what you would expect from a local entity. For example you can have a local block of code:
and a is accessible within the block, but b isn't accessible outside the block and so the println(b) fails. Notice that a block of code acts like a function that you don't have to call – it is a manually implemented inline function, see the next chapter.
The same holds for a local function:
In this case the println(a) works, as the local function has access to the containing functions variables and parameters but the println(b) fails because the containing function cannot access any variables or parameters of the local function. Also notice that you can't call a local function before it has been declared.
Local functions are useful when a function or a method grows too big. Methods and functions should never have more than a few tens of lines of code, but often the idea of breaking a method up into utility functions doesn't fit in with the rest of the structure. The answer is to split the method into a number of local functions which only it can call. In this way the method gets its own private utility function library.
Local functions are implemented as anonymous classes with a single method that corresponds to the function. In Java and hence for any language that compiles to the JVM, a function is always part of a class and hence is a method. The Kotlin compiler hides this from you and most of the time it doesn't matter. The only time it does matter is if efficiency is an issue. In this case you need to consider using inline functions – see the next chapter.
Functions have parameters and Kotlin functions have more sophisticated parameters than many languages, but first we need to look at the basic way that they work.
In Kotlin all parameters are passed by value, as is the case in Java.
What this means is that if you pass a variable to a function, the value it contains is converted into a local read-only copy. This has a number of consequences. The first is that you cannot use a parameter to pass a result back from a function. In fact you cannot change a parameter's value within a function as it is treated as a val and is read-only.
If you pass a variable that references an object, then the reference is passed by value, and within the function the parameter references the same object. What this means is that you cannot change the parameter within the function – it is still read-only - but you can change properties of the object that it references.
Notice that myFunction changes myProperty which makes it look as if the object has been passed by reference and in a weak sense it has.
Notice also that you can use a reference to an object to get results out of a function, but generally this isn't a good idea. Use a data object to collect the multiple values as properties and return it if you need multiple results. If you put this together with destructuring it looks exactly like a function that has multiple results.
Default, Named & Variable Parameters
Let's go through the possible variations on simple parameters in order of increasing sophistication.
If you simply specify some parameters then you always have to specify their type as there is no way the system can infer this until the function is used:
You can provide default values for parameters, but you still to specify the type even if this could be inferred:
Default values allow you to leave out parameters, and this means you don't have to implement an overloaded function for each subset of parameters you want to support.
However, Kotlin doesn't provide any way of indicating omitted parameters, so you can't use a default for parameter 1 and then specify a value for parameter 2. The first parameter you specify is always parameter 1. What this means is that you need to put default parameters at the end of the list of parameters.
Once the call uses a default parameter it uses all of the remaining parameters as default..
calling this with sum(1) sets a to 1 there is no way to make it set b to 1 while accepting the default for a.
If you have a method with default parameters then it can be overridden, but you can't change the defaults and you don't specify them in the redefinition. That is, the new function has the same defaults as the old function.
If you call a function with defaults from Java you have to call the function with all parameters used i.e. you cannot rely on the defaults.
|Last Updated ( Monday, 11 September 2017 )|