Traits
Traits in Groovy++ is extremely powerful but still simple tool for composition of functionality by small pieces. Technically speaking it is Java interface with static methods and possible default implementation of some methods
When non-abstract class implements such interface it can elect not to provide implementation for methods, which already has default implementation. In this case the default implementation will be reused.
Groovy++ traits are different (and somehow simpler but may be a bit less powerful) than Scala traits. Groovy++ does not create any sort of multiple inheritance
We will start with simplest possible example - Iterator, which always throw UnsupportedOperationException on attempt to remove
@Trait class NonRemovingIteraror<T> implements Iterator<T>{
void remove () {
throw new UnsupportedOperationException("remove() does not supported by iterator")
}
}
Let us see a little bit more complex example and define interface for objects, which can be accessed by index. It will have
* three abstract methods get, set and size
* two methods with implementation getAt/putAt, which will allow array like access operators
* method iterator to implement Iterable interface
* property length to be synonymous to method size
```
@Trait abstract class Indexable implements Iterable {
abstract int size ()
abstract T get(int index)
abstract void set(int index, T value)
T getAt(int index) { get(index) }
int getLength () { size() }
void putAt(int index, T value) { set(index, value) }
Iterator<T> iterator (){
(NonRemovingIteraror)[ curIndex: 0, hasNext: { curIndex < length }, next: { get(curIndex++) } ]
}
}
Now we can define class implementing this interface. Imagine that for some reason we need to define wrapper around an array. There are three methods we need to implement get/set/size
class ArrayWrapper implements Indexable{
private T [] array
ArrayWrapper(T[] array){
this.array = array
}
T get(int index){ array[index] }
void set(int index, T value){ array[index] = value }
int size () { array.length }
}
And, voila, everything we defined in the trait is at our disposal using this class (array like access and iterator method).
def wrapper = new ArrayWrapper (new String[5])
wrapper [3] = "mama mia"
assert wrapper[3] == "mama mia"
for(e in wrapper){
println e?.toUpperCase ()
}
To make sure that this approach make some sense we can also implement ListWrapper.
class ListWrapper implements Indexable {
private List list
ListWrapper(List<T> list){
this.list = list
}
T get(int index){ list[index] }
void set(int index, T value){ list[index] = value }
int size () { list.size() }
} ``` Of course, the only real gain in our first example is that Indexable is interface in oppose to abstract class. But truly speaking it is a huge gain in terms of building clean class hierarchy.
Let us imagine now that we also want to have another interface, which let us to associate any meta data with implementing objects. Something like
@Trait WithMetaData {
FHashMap<String,Object> metaData
}
Properties defined in traits defines getter/setter methods. What is even more important these methods does not require default implementation - if class implementing the interface does not provide own implementation of getter/setter like method then compiler will create internal field and natural implementation With traditional Java approach we could do it either by placing it in the root of class hierarchy or by reimplementing it in many subclasses. Life becomes even more terrible when we want to have several interfaces with implementation simultaniously. For example, ``` @Trait class ConvertibleToByteArray implements Serializable { byte [] toByteArray () { ByteArrayOutputStream bos = [] ObjectOutputStream out = [bos] out.writeObject this out.close() bos.toByteArray() }
static Object fromByteArray(byte [] bytes) {
def inp = new ObjectInputStream(new ByteArrayInputStream(bytes))
def res = inp.readObject()
inp.close()
res
}
} ```
Traits can also contains static methods. This is very powerful tool in combination with extension methods With Groovy++ we can easily implements both without touching root of hierarchy at all. ``` class SuperHashMap extends HashMap implements WithMetaData, ConvertibleToByteArray {}
SuperHashMap hm = [:] hm.metaData = hm.metaData.put("id", 239) hm.toByteArray () ```