61 lines
2.9 KiB
Plaintext
61 lines
2.9 KiB
Plaintext
h1. Motivation
|
|
|
|
In *Java*, we are used to classes named {{\*Utils}}: {{FileUtils}}, {{StringUtils}} an so on. The famous {{java.util.Collections}} belongs to the same breed. And the unpleasant part about these {{Utils}}\-classes is that the code that uses them looks like this:
|
|
{code}
|
|
// Java
|
|
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))
|
|
{code}
|
|
Those class names are always getting in the way. We can use static imports and get this:
|
|
{code}
|
|
// Java
|
|
swap(list, binarySearch(list, max(otherList)), max(list))
|
|
{code}
|
|
This is a little better, but we have no or little help from the powerful code completion of the IDE. It would be so much better if we could say
|
|
{code}
|
|
// Java
|
|
list.swap(list.binarySearch(otherList.max()), list.max())
|
|
{code}
|
|
But we don't want to implement all the possible methods inside the class {{List}}, right?
|
|
|
|
So, [Kotlin] (after *C#* and *Gosu*) introduces _extension functions_: a function declared outside the class {{List}} may be contributed to this type as an extension. This is denoted by prepending the _receiver_ type (the one being extended) to the function name:
|
|
{jet}
|
|
fun List<Int>.swap(x : Int, y : Int) {
|
|
val tmp = this[x] // 'this' corresponds to the list
|
|
this[x] = this[y]
|
|
this[y] = tmp
|
|
}
|
|
{jet}
|
|
The *this* keyword inside an extension function corresponds to the _receiver object_ (the one that is passed before the dot). Now, we can call such a function on any {{List<Int>}}:
|
|
{jet}
|
|
val l = list(1, 2, 3)
|
|
l.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'l'
|
|
{jet}
|
|
|
|
Of course, this function makes sense for any {{List<T>}}, and we can make it generic:
|
|
{jet}
|
|
fun <T> List<T>.swap(x : Int, y : Int) {
|
|
val tmp = this[x] // 'this' corresponds to the list
|
|
this[x] = this[y]
|
|
this[y] = tmp
|
|
}
|
|
{jet}
|
|
We declare the generic type parameter _before_ the function name for it to be available in the _receiver_ type expression. See [Generic functions|Generics#Generic functions].
|
|
|
|
h2. Extension functions are resolved statically
|
|
|
|
To avoid confusion, we would like to emphasize that extension functions are resolved _statically_, i.e. they are not *virtual* by receiver type. On the other hand they can be member functions on some class and thus can be virtual in that class hierarchy.
|
|
|
|
h1. Similar features in other languages
|
|
|
|
*AspectJ* has [inter-type declarations|http://www.eclipse.org/aspectj/doc/released/adk15notebook/ataspectj-itds.html].
|
|
|
|
*Groovy* has [metaclasses|http://groovy.codehaus.org/JN3525-MetaClasses].
|
|
|
|
*Scala* makes heavy use of implicit conversions, i.e. wraps values into adapters at runtime.
|
|
|
|
*[C#|http://msdn.microsoft.com/en-us/library/bb383977.aspx]* and *[Gosu|http://gosu-lang.org/doc/wwhelp/wwhimpl/api.htm?&context=gosu&src=enhancements&topic=Using_Enhancements]* have extension functions implemented similarly to our approach.
|
|
|
|
h1. What's next
|
|
|
|
* [Function literals]
|
|
* [Type-safe Groovy-style builders] |