The Programmers Guide To Kotlin: Advanced Functions
Written by Mike James   
Monday, 11 September 2017
Article Index
The Programmers Guide To Kotlin: Advanced Functions
this & Extension Functions
Infix Functions

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 Second Edition

kotlin2e360

You can buy it from: Amazon

Contents

  1. What makes Kotlin Special
  2. The Basics:Variables,Primitive Types and Functions 
  3. Control
         Extract: If and When 
  4. Strings and Arrays
  5. The Class & The Object
  6. Inheritance
  7. The Type Hierarchy
  8. Generics
  9. Collections, Iterators, Sequences & Ranges
        Extract: Iterators & Sequences 
  10. Advanced functions 
  11. Anonymous, Lamdas & Inline Functions
  12. Data classes, enums and destructuring
        Extract: Destructuring 
  13. Exceptions, Annotations & Reflection
  14. Coroutines
        Extract: Coroutines 
  15. Working with Java
        Extract: Using Swing ***NEW!

<ASIN:B096MZY7JM>

Free Functions

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:

fun name(parameters){ body of function }

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:

fun name(parameters)= expression

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: 

@file:JvmName("class name")

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:

fun myMethod(){
 var a=0
 {
  println(a)
  var b=0 
 }
 println(b)
}

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:

var a=0
fun myFunction(){
     var b=0
     println(a)
 }
 myFunction()
 println(b)

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.

Parameters

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.

 

For example:

class MyClass(var myProperty: Int=0){...
fun myFunction(myObject: MyClass) {
    myObject.myProperty = 1
    }
val myObjectA = MyClass()
myFunction(myObjectA)

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.

To summarize: 

  • All parameters are passed by value.

  • Parameters are read-only val types.

  • Object references are passed as read-only values referencing the same object.

  • This means you can change properties on an object from within a function – this isn't a good idea. 

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:

fun myFunction(p1:Int,p2:String,p3:MyClass){    
}

Defaults

You can provide default values for parameters, but you still to specify the type even if this could be inferred:

fun myFunction(p1:Int=0,p2:String="abc",
                      p3:MyClass=MyClass()){
}

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..

For example:

fun sum(a:Int=0,b:Int){return a+b}

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.

<ASIN:1871962536>



Last Updated ( Monday, 11 September 2017 )