Search

Dark theme | Light theme
Showing posts with label GroovyGoodness:MOP. Show all posts
Showing posts with label GroovyGoodness:MOP. Show all posts

March 19, 2015

Groovy Goodness: Use Constructor as Method Pointer

In Java 8 we can create a constructor reference. We must use the syntax Class::new and we get a constructor reference. This syntax is not supported in Groovy, but we can use the method pointer or reference syntax .& to turn a method into a closure. We can even turn a constructor into a closure and use it everywhere where closures are allowed.

In the following sample code we have a User class with some properties. Via the User.metaClass we can get a reference to the method invokeConstructor and turn it into a method closure:

@groovy.transform.Immutable
class User {
    String name
    int age
}


// Initial list with user defined
// using a map or Object array.
def userList = [
    // User defined as map, keys
    // are properties of User class.
    [name: 'mrhaki', age: 41], 
    
    // Object array with name and
    // age properties for User class.
    ['john', 30] as Object[]
]

// Create constructor reference.
// Result is a closure we can use in our code.
def createUser = User.metaClass.&invokeConstructor

// Invoke the collect method with our
// constructor reference. At the end
// all elements of the userList 
// are converted to new User objects.
def users = userList.collect(createUser)


assert users.name == ['mrhaki', 'john']
assert users.age == [41, 30]

Code written with Groovy 2.4.1.

February 4, 2013

Groovy Goodness: Apply Mixin to Object Instances

In Groovy we can add extra functionality to a class with so-called mixins. In the previous blog post we added extra functionality to a class, but we can also apply run-time mixins to an object instance. The syntax is slightly different, because we must use the mixin() method on the metaClass property of the object.

class Parrot {
    static String speak(Message text) {
        /"$text" Polly wants a cracker!/
    }
}

// Runtime mixin on String object instead of class. 
String s = 'Groovy is Gr8'
s.metaClass.mixin Parrot

assert s.speak() == '"Groovy is Gr8" Polly wants a cracker!'


String other = 'Groovy and Grails'
try {
    other.speak()
} catch (MissingMethodException e) {
    assert e.message.startsWith('No signature of method: java.lang.String.speak() is applicable for argument types: () values: []')
}

Written with Groovy 2.1

September 26, 2012

Groovy Goodness: Using Implicit call() Method

In Groovy we can invoke an implicit call() method on a Groovy object. We can leave out the call method name and just use (). We can use meta programming to add an implementation for the call() method to a class. In the following example script we add an implementation for the call() method with a single parameter to the String class. The implementation returns the element found at the range specified by the argument when we invoke the method:

String.metaClass.call = { range ->
    delegate[range]
}

def value = 'Groovy is Gr8'
assert value(0) == 'G'
assert value(10) == 'G'
assert value(4) == value[4]
assert value.call(1) == value(1)
assert value(0..5) == 'Groovy'

Inspired by the examples http://groovyconsole.appspot.com/view.groovy?id=21006 and http://groovyconsole.appspot.com/script/21005 we can also write our own class and implement the call() method. This can for example be used in DSLs.

class StringConverter {
    def value
    
    def value(s) {
        value = s
        this
    }

    /** Convert characters in value property if cond is true */    
    def upper(cond) {
        value = value.collect { cond(it) ? it.toUpperCase() : it }.join()
    }

    def call(callable) {
        callable
    }
}

def converter = new StringConverter()
converter.with {
    value 'mrhaki' upper { it < 'm' }
    // Equivalent to:
    // value('mrhaki') upper { it < 'm' }
    // or
    // value('mrhaki').call(upper { it < 'm' })
    // or
    // value('mrhaki').call(upper({ it < 'm' }))
}
assert converter.value == 'mrHAKI'

converter.with {
    value('jdriven') upper { it == 'j' || it == 'd' }

    assert value == 'JDriven'
}

(Code written with Groovy 2.0.4)

January 18, 2010

Groovy Goodness: Override getProperty Method with Dynamic Groovy

Groovy's ExpandoMetaClass features allows us to override the getProperty() method for a class. This method is invoked if we want to access a property for an object. We can look up existing properties and return their result, but we can also write behaviour for the situation when the property doesn't exist.

class User {
    String username
}

User.metaClass.getProperty = { String propName ->
    def meta = User.metaClass.getMetaProperty(propName)
    if (meta) {
        meta.getProperty(delegate)
    } else {
        'Dynamic property for User'
    }
}

def mrhaki = new User(username: 'mrhaki')
def hubert = new User(username: 'hubert')

assert 'mrhaki' == mrhaki.username
assert 'Dynamic property for User' == mrhaki.fullname

December 22, 2009

Groovy Goodness: Implementing MetaClass Methods with Same Name but Different Arguments

Using the metaClass property of a class we can add new methods or override existing methods of the class. If we want to add a new methods that have the same name, but different arguments, we can use a shortcut notation. Groovy allows us to chain multiple leftShift() (<<) methods together.

String.metaClass.groovy << { Integer number ->
    delegate * number
} << { String s ->
    delegate + s
} << { ->
    delegate + ' Groovy rocks.'
}

assert 'GroovyGroovy' == 'Groovy'.groovy(2)
assert 'Hello world from Groovy' == 'Hello world'.groovy(' from Groovy')
assert 'It is true. Groovy rocks.' == 'It is true.'.groovy()

December 21, 2009

Groovy Goodness: Override and Use Old Implementation with Dynamic Methods

We can add new methods to classes with the metaClass property. But what if we want to use the old implementation for some cases? We can save the original method implementation in a variable and use it in our new implementation. The best way to get the original method is to access metaClass and invoke the getMetaMethod() method. We save this method in a variable. In the new implementatation we can use the invoke() method on the saved method to invoke the original code.

def savedToUpperCase = String.metaClass.getMetaMethod('toUpperCase', [] as Class[])
String.metaClass.toUpperCase = { -> 
    def result = savedToUpperCase.invoke(delegate)    
    if (delegate =~ /Groovy/) {
        result + ' Oh, yeah man! Groooovy...'
    } else {
        result
    }
}

assert 'A SIMPLE STRING' == 'A simple string'.toUpperCase()
assert 'THIS IS GROOVY. Oh, yeah man! Groooovy...' == 'This is Groovy.'.toUpperCase()

December 20, 2009

Groovy Goodness: Adding or Overriding Methods with ExpandoMetaClass

In Groovy we can add new methods to classes with ExpandoMetaClass using the leftShift (<<) operator. If the method already exists we get an error. To override an existing method we can use the equal operator (=). But we can also use the equal operator (=) to add a new method, but then we don't get an error when the method already exists.

Integer.metaClass.eights << { delegate * 8 }
assert 32 == 4.eights()

Integer.metaClass.hundreds = { delegate * 100 }
assert 200 == 2.hundreds()

try {
    Integer.metaClass.toString << { 'Groovy' } // Cannot use << for existing method.
    assert false
} catch (e) {
    assert 'Cannot add new method [toString] for arguments [[]]. It already exists!' == e.message
}

Integer.metaClass.toString = { 'Groovy' }
assert 'Groovy' == 100.toString()

December 14, 2009

Groovy Goodness: Add Static Methods with MetaClass to Classes

Groovy's dynamic nature makes it easy to extend classes with new behaviour. One of the ways is to add methods or property to the metaClass property of a class. We can use the same mechanism to add static methods to the class. We only have to specify the keyword 'static' when we define the new method.

class User {
    String username, email
    String toString() { "$username, $email" }
}

User.metaClass.static.new = { u, e ->
    new User(username: u, email: e)
}

def u = User.new('mrhaki', 'mail@host.com')
assert 'mrhaki' == u.username
assert 'mail@host.com' == u.email
​

December 6, 2009

Groovy Goodness: Add Methods to Interfaces with ExpandoMetaClass

We can add new behaviour to classes with the ExpandoMetaClass, but we can do the same for interfaces. We can add new methods to interfaces through the metaClass property.

interface Nothing { }
class Simple implements Nothing { }

Nothing.metaClass.groovyShoutOut = { -> "Groovy is awesome!" }

def s = new Simple()
assert 'Groovy is awesome!' == s.groovyShoutOut()

November 30, 2009

Groovy Goodness: Use a Category and Get a Return Value

With the use() method in Groovy we execute a closure in the scope of a Category or list of Categories. The last statement of the closure is the return value for the use() method. So we can get a result from the use() method and use it in our program.

class StringCategory {
    static String groovy(String self) {
        self + ' Yeah man.'
    }
}

def returnedValue = use(StringCategory) {
    def s = 'A simple String.'
    s.groovy()
}

assert 'A simple String. Yeah man.' == returnedValue

November 29, 2009

Groovy Goodness: A Bit of metaClass DSL

When we add behaviour to classes with the metaClass property we can use a simple DSL. We pass a closure to the metaClass method where we group the definitions for the new behaviour. If we add multiple methods with the same name we must use <<, otherwise we can just set a closure to the method name. To add a static method we use 'static' and define the methods.

String.metaClass {
    or << { String s -> delegate.plus(' or ').plus(s) }
    or << { List l -> delegate.findAll("(${l.join('|')})") }
    and { String s -> delegate.plus(' and ').plus(s) }
    'static' {
        groovy { 'Yeah man!' }
    }
}

assert 'Groovy or Java?' == ("Groovy" | "Java?")
assert ['o', 'o', 'y'] == ("Groovy" | ['o', 'y'])
assert 'Groovy and Java!' == ("Groovy" & "Java!")

assert 'Yeah man!' == String.groovy()

November 23, 2009

Groovy Goodness: Create Dynamic Methods

In Groovy we can write classes and write an implementation for methods we don't even know exist. We must implement the method methodMissing() which is invoked when other methods cannot be found. Because methodMissing() is invoked each time another method cannot be found, it can be expensive, but we can use the metaClass property to cache the new method. Next time the cached version is used.

class LanguageList {
    def list = ['Java', 'Groovy', 'Scala']

    // Set metaClass property to ExpandoMetaClass instance, so we 
    // can add dynamic methods.
    LanguageList() {
        def mc = new ExpandoMetaClass(LanguageList, false, true)
        mc.initialize()
        this.metaClass = mc
    }
    
    def methodMissing(String name, args) {
        // Intercept method that starts with find.
        if (name.startsWith("find")) {
            def result = list.find { it == name[4..-1] }
            // Add new method to class with metaClass.
            this.metaClass."$name" = {-> result + "[cache]" }
            result
        } else {
            throw new MissingMethodException(name, this.class, args)
        }
    }
}

def languages = new LanguageList()
assert 'Groovy' == languages.findGroovy()
assert 'Scala' == languages.findScala()
assert 'Java' == languages.findJava()
assert !languages.findRuby()

assert 'Groovy[cache]' == languages.findGroovy()
assert 'Scala[cache]' == languages.findScala()
assert 'Java[cache]' == languages.findJava()

November 20, 2009

Groovy Goodness: Add Methods Dynamically to Classes with ExpandoMetaClass

We can add new behaviour, like methods, to classes in Groovy dynamically. So this means a method is not added to the class definition in the source code, but to the class definition while an application is already running. Groovy adds a metaClass property to all classes for this purpose. The type of this property is ExpandoMetaClass. We can assign methods (also static), properties, constructors to the metaClass property and the defined behaviour is added dynamically to the class definition. After we have added our behaviour we can create a new instance of the class and invoke the methods, constructors and access the properties just like we are used to do.

// We add the method rightShift to the List class. 
// The implementation is simply calling the remove method of List with
// the provided argument.
List.metaClass.rightShift {
    delegate.remove it
}

def list = ['one', 'two', 'three', 'four']
assert 4 == list.size()

list.rightShift 'two'
assert 3 == list.size()
assert ['one', 'three', 'four'] == list

// Operator overloading in action: rightShift is >>
list >> 'one'
assert 2 == list.size()
assert ['three', 'four'] == list


// We can also add behaviour to a specific instance instead of class.
// Notice we use the instance list instead of class List to assign 
// method groovy to metaClass property.
list.metaClass.groovy {
    delegate.collect { it + ' groovy' }
}

assert ['three groovy', 'four groovy'] == list.groovy()

def newList = ['a', 'b']
try {
    newList.groovy()  // groovy method was added to list instance not List class.
    assert false
} catch (e) {
    assert e instanceof MissingMethodException
}

October 30, 2009

Groovy Goodness: Invoke Methods Dynamically

In Groovy we can invoke a method with a name assigned to a variable. We use the GString notation to invoke the method with the variable name. We can pass parameters to the method just like when we use the method name directly.

def s = "Groovy is so much fun"
def methods = ['Upper', 'Lower']
def result = methods.collect { s."to${it}Case"() }
assert 'GROOVY IS SO MUCH FUN' == result[0]
assert 'groovy is so much fun' == result[1]

// We can pass parameters just like a normal method invocation.
def method = 'count'
def param = 'o'
assert 3 == s."$method"(param)  // s.count('o').

October 17, 2009

Groovy Goodness: Check If Method or Property is Available

In Groovy we can easily see if a method or property is available for an object. We can use the respondsTo() and hasProperty() methods to see if the method or property can be invoked or accessed. We can not check for methods or properties which are added by overriding the methodMissing()/propertyMissing() or invokeMethod()/getProperty() methods.

class Simple {
    String language
    
    def whatDoYouSpeak() {  
        language
    }
    
    def say(String text) {
        "You say $text in $language"
    }
}

def s = new Simple(language: 'Groovy')

assert s.metaClass.respondsTo(s, 'whatDoYouSpeak')
assert Simple.metaClass.respondsTo(s, 'say')
assert s.metaClass.respondsTo(s, 'say', String)
assert !s.metaClass.respondsTo(s, 'say', Integer)  // No say(Integer) method.
assert Simple.metaClass.respondsTo(s, 'toString')  // Method in parent object.

assert s.metaClass.hasProperty(s, 'language')
assert Simple.metaClass.respondsTo(s, 'getLanguage')  // Get/set methods are generated.
assert s.metaClass.respondsTo(s, 'setLanguage')

October 3, 2009

Groovy Goodness: Mixin Functionality to Classes

In a previous blog post we learned how to use Categories to add functionality to existing class. We had to use the use keyword to define a block in which the Category was valid. But we can also add new functionality with the @Mixin compile-time annotation or at runtime with the mixin() method (GDK extension to Class).

class Pirate {
    def talk(text) {
        "Aargh, walk the plank. ${text}"
    }
}

// Compile time mixin to Talk class. This add all
// methods from Pirate to Talk.
@Mixin(Pirate)  
class Talk {}

assert 'Aargh, walk the plank. Give me a bottle of rum.' == new Talk().talk("Give me a bottle of rum.")
import org.apache.commons.lang.StringUtils

class Parrot {
    static String speak(String text) {
        /Parrot says "$text"/
    }
}

// Runtime mixin on String class. 
// mixin() is a GDK extension to Class.
String.mixin Parrot, StringUtils

assert 'Parrot says "mrhaki"' == 'mrhaki'.speak()
assert 'Groovy is so much...' == 'Groovy is so much fun.'.abbreviate(20)  // StringUtils mixin.

October 2, 2009

Groovy Goodness: Expando as Dynamic Bean

The Expando class in Groovy is sort of a dynamic bean. We can add properties and we can add closures as methods to an instance of the Expando class. This is useful if we don't want to create a new class for a simple bean.

def user = new Expando(username: 'mrhaki')
assert 'mrhaki' == user.username

// Add an extra property.
user.email = 'email@host.com'
assert 'email@host.com' == user.email

// Assign closure as method. The closure can
// take parameters.
user.printInfo = { writer ->
    writer << "Username: $username"
    writer << ", email: $email"
}

def sw = new StringWriter()
user.printInfo(sw)
assert 'Username: mrhaki, email: email@host.com' == sw.toString()

September 29, 2009

Groovy Goodness: Use Categories to Add Functionality to Classes

Categories allow us to add extra functionality to classes, even those classes we didn't develop ourselves. A Category class contain static methods. The first argument of the method determines the type the method can be applied to.

We can also use the @Category transformation annotation to make a class into a Category. The class doesn't have to define static methods anymore, but we can use instance methods. The parameter of the @Category annotation defines for which class the Category is.

The Category can be applied to a specific code block with the use keyword. Only within the code block the rules of the Category are applied.

class Speak {
    static String shout(String text) {  // Method argument is String, so we can add shout() to String object.
        text.toUpperCase() + '!'
    }
    
    static String whisper(String text, boolean veryQuiet = false) {
        "${veryQuiet ? 'sssssssh' : 'sssh'}.. $text"
    }
    
    static String army(String text) {
        "$text. Sir, yes sir!"
    }
}

use (Speak) {
    assert 'PAY ATTENTION!' == "Pay attention".shout()
    assert 'sssh.. Be vewy, vewy, quiet.' == "Be vewy, vewy, quiet.".whisper()
    assert 'sssssssh.. Be vewy, vewy, quiet.' == "Be vewy, vewy, quiet.".whisper(true)
    assert 'Groovy rocks. Sir, yes sir!' == "Groovy rocks".army()
}

// Or we can use the @Category annotation.
@Category(String) 
class StreetTalk {
    String hiphop() {
        "Yo, yo, here we go. ${this}"
    }
}

use(StreetTalk) {
    assert 'Yo, yo, here we go. Groovy is fun!' == 'Groovy is fun!'.hiphop()
}

// We can use static methods from other Java classes.
use (org.apache.commons.codec.digest.DigestUtils) {
    assert '900150983cd24fb0d6963f7d28e17f72' == 'abc'.md5Hex()
}