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.