Enhancements provide a way to add methods and properties to existing types. They are similar to Extension Methods in C#, but do not need to be explicitly imported.
Enhancements must be defined in files ending in the .gsx suffix. They cannot be defined inline within
other gosu resources presently.
Here is an example enhancement that adds a method to java.lang.String:
package example
enhancement MyStringEnhancement : String {
function printMe() {
print( this )
}
}
In enhancements, the this symbol refers to the enhanced type, rather than the enhancement.
Given the enhancement above, you can invoke the printMe() method on Strings like this:
"Hello Enhancements".printMe()
example.MyStringEnhancement.print( "Hello World" )
Enhancements are
statically dispatched. This means they cannot be used to implement interfaces or to achieve polymorphism (unless you use a filthy, awful hack.)
We informally refer to using enhancements as "donkey patching" a class. They are kind of like a monkey patch, but a bit more formalized. Only a bit.
List<T>:
package example
uses java.util.List
enhancement MyListEnhancement<T> : List<T> {
function firstAndLast() : List<T> {
return {this.first(), this.last()}
}
}
This method will now be available on all generic lists, and will be properly typed.
Unlike in Java, type variables can be used in general expressions in Gosu. In Enhancements, the type
variables are statically, rather than dynamically, reified, much like enhancement methods are statically, rather than
dynamically dispatched. The enhancement method toTypedArray():T[] on Iterable<T>
demonstrates this:
var lstOfStrings : List<String> = {"a", "b", "c"}
var arrOfStrings = lstOfStrings.toTypedArray() //returns a String[]
var lstOfObjs : List<Object> = lstOfStrings //type variables are covariant in Gosu, see generics
var arrOfObjs = lstOfObjs.toTypedArray() //returns an Object[]
This "best effort" reification usually does what you want, but can occasionally lead to surprising results.
A really neat trick with enhancements is that you can enhance parameterized types:
package example
uses java.util.*
enhancement MyListOfDatesEnhancement : List<Date> {
function allBetween( start : Date, end : Date ) : List<Date>{
var lst = new ArrayList<Date>()
for( d in this ) {
if( start <= d and d <= end ) {
lst.add( d )
}
}
return lst
// NOTE: you would normally write this using blocks:
// return this.where( \ d -> start <= d and d <= end )
}
}
This is how all lists of comparable objects have the sort() method on them, while other lists do not.
Sweet.