Monday, February 12, 2018

Groovy: Advanced Concepts: Generic Abstraction

In addition to process and data abstraction, Groovy offers a form of generic abstraction.  It accomplishes this by using the underlying Java generics mechanism.  This allows typed collections and other classes that specify at compile time a specific class or interface that can be used with a specific generic.
Generics were added in 2004 with the release of Java 1.5.  Prior to this, Java collections targeted Object only.  This meant that the developer would need to perform a series of casts to coerce the value provided from the collection to the appropriate type.  Not only was this messy, it also offered a higher likelihood of runtime errors due to improperly trying to cast an object from one type to another.
ArrayList old = new ArrayList();
old.add(“A string”);
 
// The required cast to use the stored object as a String
String aString = (String)old.get(0);
 
ArrayList<String> generic = new ArrayList<String>();
generic.add(“a string”);
 
// Accessing an element from a generic does not require
// the cast operation
aString = generic.get(0);
Groovy inherits this Java generics behavior directly, allowing the developer to use the Java generics.
def list = new ArrayList<String>() as ArrayList<String>
list.add(/a string/)
String value = list.get(0)
Not only does this genericity apply to provided classes, the developer can create classes that utilize the same mechanism.  The following example creates a new collection-like object that takes a generic type T.  At compile time, the compiler will check to see that any objects added are of the type or subclass of T as defined in the code.
class GroovyGenericClass<T>
{
    private ArrayList<T> items = new ArrayList<T>()
   
    void add(T t)
    {
        items.add(t)
    }
}
Java generics also offer the ability to specify a base class or interface that a class must extend or implement to be compatible with the generic class.  An interface is a definition of expected behavior of a class, through the definition of methods and constants, that any implementing class must conform to.  In the context of generics, this allows the developer of the generic to define a specific behavior or method that can be expected of any class that is used in the generic.
// Require any class used in the generic to extend
// Comparable (in this case Comparable is an interface
// so the class must implement it)
class GroovyGenericClass<T extends Comparable>
{
    private ArrayList<T> items = new ArrayList<T>()
 
    void add(T t)
    {
        items.add(t)
    }
   
    boolean equals(int index, T t)
    {
        // The compareTo method is defined in the
        // interface Comparable
        return items.get(index).compareTo(t) == 0
    }
}

No comments: