Extension functions?
Table of Contents
Concurnas has support for extension functions. These allow for functionality to be added to classes without needing to interact with the class hierarchy (e.g.extending the class etc). They are a convenient alternative to having to use utility functions/methods/classes which take an instance of a class and often permit a more natural way of interacting with objects.
def String repeater(n int){//this is an extension function
return String.join(", ", java.util.Collections.nCopies(n, this))
}
"value".repeater(2) // returns: 'value, value'
Public properties or methods of the extended class may be referenced inside the body of the extension function. Non public items may not be referenced:
class MyClass(public name String){
def amethod() = 'MyClass Method'
private unseen = 1//not accessible from the extension function below
}
def MyClass extFunc(){
"" + [this.name, this.amethod()]
}
new MyClass('dave').extFunc() //resolves to '[dave, MyClass Method]'
Extension Methods?
Extension methods are the same as extension functions, just defined within a class. For example:
class MyClass(public name String){
def amethod() = 'MyClass Method'
}
class AnotherClass{
def MyClass extFunc(){
" + [this.name, this.amethod()]
}
def dotask(){
new MyClass('dave').extFunc()
}
}
new AnotherClass().dotask()//resolves to '[dave, MyClass Method]'
Extension methods may only be defined as private, protected.
This keyword?
The this
keyword can be used inside extension functions/methods and refers to the instance of the class being extended. In the case of an extension method it may be qualified in order to refer to the containing class:
class MyClass(){
def amethod() = 'MyClass Method'
}
class AnotherClass{
def amethod() = 'AnotherClass Method'
def MyClass extFunc(){
"" + [this.amethod(), //this defaults to refer to the MyClass instance
this[MyClass].amethod(),//explicitly directed to MyClass
this[AnotherClass].amethod()]//explicitly directed to AnotherClass
}
def dotask(){
new MyClass().extFunc()
}
}
new AnotherClass().dotask()//resolves to '[MyClass Method, MyClass Method, AnotherClass Method]'
Note that in cases where an extension method/function has the same input signature as the method associated with the object being invoked then the extension function will take priority:
class MyClass{
def countdown(a int) => "MyClass instance: {a}"
}
def MyClass countdown(a int){
""+ 0 if a == 0 else "{a}, {this.countdown(a-1)}"
}
res = new MyClass() countdown 5
//res == "5, 4, 3, 2, 1, 0" and NOT "10, MyClass instance: 9"
Super Keyword?
The super
keyword can be used inside extension functions/methods in the same way as the this
keyword, however, unlike the this
keyword subsequent method invocations always refer to the instance of of the class being extended. This thus explicitly avoids an otherwise recursive call in the case of a matching input signature between the extension function/method and method associated with the object upon which the invocation is taking place.
class MyClass{
def countdown(a int) => "MyClass instance: {a}"
}
def MyClass countdown(a int){
""+ 0 if a == 0 else "{a}, {super.countdown(a-1)}"
}
res = new MyClass() countdown 5
//res == "10, MyClass instance: 9" and NOT "5, 4, 3, 2, 1, 0"
Resolution of extension functions vs instance methods?
In cases where an extension function overrides the definition of a method of the class it's extending, the extension function will be invoked in place of the instance method:
class MyClass(){
def amethod() = 'instance method'
}
def MyClass amethod(){
'Extension function'
}
new MyClass().amethod() //resolves to 'Extension function'
Extension functions with generics?
Class or local generics may be used as par normal for extension functions.
class MyClass<X>{
def amethod() = 12
}
fun MyClass<X> repeater<X>( ) {//extension function with extendee qualified via local generic type
""+ [amethod(), this.amethod()]
}
new MyClass<String>().repeater<String>()// resolves to: '[12, 12]'
As per usual, qualification of generic types may also be inferred:
class MyClass<X>{
def amethod() = 12
}
fun MyClass<X> repeater<X>( ) {//extension function with extendee qualified via local generic type
""+ [amethod(), this.amethod()]
}
new MyClass<String>().repeater()// resolves to: '[12, 12]' as local generic is qualified to String via inference
Extension functions as operator overloaders?
Extension functions can also be used in order to provide operator overloading. For example, in order to overload +
plus for an Arraylist
:
from java.util import ArrayList
fun ArrayList<X> plus<X>(toAdd X) {
this.add(toAdd)
this
}
ar = new ArrayList<int>()
ar = ar + 12 //operator overload
ar//resolves to '[12]'
Extension functions on primitive types?
In addition to object classes, extension functions can be defined for primitive types.
def int meg() => this * 1024 * 1024
12 meg //-> 12582912
Auto boxing/unboxing is performed automatically:
def Integer meg() => this * 1024 * 1024
12 meg //-> 12582912
References?
References to extension functions/methods can be made as par usual:
def String repeater(n int){//this is an extension function
return String.join(", ", java.util.Collections.nCopies(n, this))
}
rep = "value".repeater&(2) //creates a reference
rep() // returns: 'value, value'
Note that bindings to extension functions are created at compile time, not runtime. Thus changes to class hierarchies etc will require a recompilation of the extension function and related code in order to be incorporated. In practice this is not a significant problem, but it is something to be aware of nevertheless.