|The Programmers Guide To Kotlin: Advanced Functions|
|Written by Mike James|
|Monday, 11 September 2017|
Page 2 of 3
The problem with specifying default parameter values with only positional parameters leads us on to the next variation - named parameters. You don't have to do anything extra, you simply use the parameters as if they were variables within the function call.
This sets values for the first and third parameter and the second takes its default value.
Using named parameters in this way not only makes it possible to be selective about which parameters use their defaults, it also makes function calls more understandable, but only if you give parameters sensible meaningful names – which is usually a difficult task.
The final parameter trick that Kotlin has for us is variable argument lists.
The basic idea is that one of the parameters can be marked as a vararg. You can only have one vararg parameter and it is usually the last. The reason is that when the vararg parameter begins accepting values it continues until the closing parenthesis is reached. This means that the only way to set any parameters beyond the vararg is by name. The values stored in the vararg are treated as an array of the indicated type. Notice that varargs can only be of a single type and its sub-types. You can think of vararg as an instruction to pack all of the arguments that follow into an array.
Of course it is up to you to make sure that you don't try and use an array element that doesn't exist. You can use generics with vararg but of course this restricts what you can do with the parameters. If you want to allow mixed parameters then you can also use Any and casts:
This & Methods
When you call a method:
this is an alternative way of writing:
That is, the object instance the method is called on is conceptually the same as an additional parameter. The additional parameter is usually named this and it is refereed to as the receiver or the call context and of course, it isn't passed as parameter, it is just made available within the object.
In most cases you don't have to use this within the method to refer to the instance, because it is assumed that your references are to the current instance.
For example if you are writing a class:
The reference to myProperty in myMethod is shorthand for:
but as it within the class declaration it is assumed. Sometimes it is a good idea to explicitly write this to distinguish what is and what is not a member of the class.
You have to write this.myProperty to distinguish it from the myProperty parameter. In most cases this isn't necessary.
As already stated you can think of this as an extra parameter that is set to reference the object that the method is currently working with. This is standard across most object oriented programming languages but Kotlin goes a little further.
In Kotlin you can have inner classes and this means that in a method you can have an inner and an outer context. The same applies to extension functions and function literals with a receiver – see the next chapter.
Normally this refers to the innermost enclosing scope i.e. the object you would most expect it to refer to. You can make this reference an outer scope by using a label. By default classes have labels that use the same name as the class.
A simple example is difficult to construct because at the least you need is one inner class:
In this case we have an outer class with an inner class which has a property of the same name as the outer class and a single method that prints this.myProperty, this@MyInnerClass.myProperty and this@MyOuterClass.myProperty.
To try this out we need an instance of the outer class:
What you see is 1,1,0 corresponding the inner class's property twice, followed by the outer class’s property.
This is a potentially very confusing but occasionally useful feature of the language.
In Kotlin you can add a method to any class without needing access to the class declaration. This seems to be almost magic but if you keep in mind the way that methods and this works it seems simple.
An extension method discovers the instance it is to work with via this which is set to reference the instance or receiver when the method is called. Methods get their this parameter by virtue of being part of a class, but any function that has access to a parameter that provides a reference to the current instance can look like a class member even if it isn't.
Kotlin provides the this mechanism to any function even if it isn't a member of the class – it simply has to be declared in association with the class.
myExtension is not part of MyClass but it can now be called as if it was:
It looks as if myExtension is a method of myObject but of course it isn't. The call:
is equivalent to:
where this is the first parameter of the function call. This is a simple syntactic change.
Notice that this implies that the extension method has no access to the internal workings of the class, only its public methods and properties.
You might think that this would limit the usefulness of extension methods but the ability to add methods to classes that you don't have access to including built-in classes such as Int and String is so useful that the Kotlin standard library has lots of extension methods.
Extension methods can, of course, be Generic.
Now we come to the question of how the compiler knows that there is a suitable extension?
Suppose we have two extensions:
and you write:
The compiler knows that there is an extension method called myExtension for ClassA and ClassB and obviously it selects the one defined on MyClassB to call for myObjectB.
The important point is that it is the type of the variable at compile time that determines which extension function is called.
That is, extensions are resolved statically at compile type rather than dynamically at runtime.
To see why this matters consider the situation where MyClassB is a subclass of MyClassA. Now if we try:
which is fine, as MyClassB can be treated as if it was a MyClassA. We now have a variable of type MyClassA referencing an object of type MyClassB and the question is, which extension method will be called:
The answer is, the one that was defined on MyClassA because this is the type of the variable, and extension methods are resolved statically on the type of the variable they are called on.
Notice that this is true of a generic extension method:
will call the extension with <Int> if the myObject variable is of type mutableList<Int> irrespective of any variance.
There are also some general points to keep in mind:
|Last Updated ( Monday, 11 September 2017 )|