public abstract class Closure<V> extends GroovyObjectSupport implements Cloneable, Runnable, GroovyCallable<V>, Serializable
Groovy allows instances of Closures to be called in a short form. For example:
def a = 1 def c = { a } assert c() == 1To be able to use a Closure in this way with your own subclass, you need to provide a doCall method with any signature you want to. This ensures that
getMaximumNumberOfParameters()
and
getParameterTypes()
will work too without any
additional code. If no doCall method is provided a
closure must be used in its long form like
def a = 1 def c = {a} assert c.call() == 1
Modifier and Type | Field and Description |
---|---|
static int |
DELEGATE_FIRST
With this resolveStrategy set the closure will attempt to resolve property references and methods to the
delegate first then the owner.
|
static int |
DELEGATE_ONLY
With this resolveStrategy set the closure will resolve property references and methods to the delegate
only and entirely bypass the owner.
|
static int |
DONE |
static Closure |
IDENTITY |
protected int |
maximumNumberOfParameters |
static int |
OWNER_FIRST
With this resolveStrategy set the closure will attempt to resolve property references and methods to the
owner first, then the delegate (this is the default strategy).
|
static int |
OWNER_ONLY
With this resolveStrategy set the closure will resolve property references and methods to the owner only
and not call the delegate at all.
|
protected Class[] |
parameterTypes |
static int |
SKIP |
static int |
TO_SELF
With this resolveStrategy set the closure will resolve property references to itself and go
through the usual MetaClass look-up process.
|
Constructor and Description |
---|
Closure(Object owner)
Constructor used when the "this" object for the Closure is null.
|
Closure(Object owner,
Object thisObject) |
Modifier and Type | Method and Description |
---|---|
Closure |
asWritable() |
V |
call()
Invokes the closure without any parameters, returning any value if applicable.
|
V |
call(Object... args) |
V |
call(Object arguments)
Invokes the closure, returning any value if applicable.
|
Object |
clone() |
Closure<V> |
curry(Object... arguments)
Support for Closure currying.
|
Closure<V> |
curry(Object argument)
Support for Closure currying.
|
Closure<V> |
dehydrate()
Returns a copy of this closure where the "owner", "delegate" and "thisObject"
fields are null, allowing proper serialization when one of them is not serializable.
|
Object |
getDelegate() |
int |
getDirective() |
int |
getMaximumNumberOfParameters() |
Object |
getOwner() |
Class[] |
getParameterTypes() |
Object |
getProperty(String property)
Retrieves a property value.
|
int |
getResolveStrategy()
Gets the strategy which the closure users to resolve methods and properties
|
Object |
getThisObject() |
boolean |
isCase(Object candidate) |
Closure<V> |
leftShift(Closure other)
Support for Closure reverse composition.
|
V |
leftShift(Object arg) |
Closure<V> |
memoize()
Creates a caching variant of the closure.
|
Closure<V> |
memoizeAtLeast(int protectedCacheSize)
Creates a caching variant of the closure with automatic cache size adjustment and lower limit
on the cache size.
|
Closure<V> |
memoizeAtMost(int maxCacheSize)
Creates a caching variant of the closure with upper limit on the cache size.
|
Closure<V> |
memoizeBetween(int protectedCacheSize,
int maxCacheSize)
Creates a caching variant of the closure with automatic cache size adjustment and lower and upper limits
on the cache size.
|
Closure<V> |
ncurry(int n,
Object... arguments)
Support for Closure currying at a given index.
|
Closure<V> |
ncurry(int n,
Object argument)
Support for Closure currying at a given index.
|
Closure<V> |
rcurry(Object... arguments)
Support for Closure "right" currying.
|
Closure<V> |
rcurry(Object argument)
Support for Closure "right" currying.
|
Closure<V> |
rehydrate(Object delegate,
Object owner,
Object thisObject)
Returns a copy of this closure for which the delegate, owner and thisObject are
replaced with the supplied parameters.
|
<W> Closure<W> |
rightShift(Closure<W> other)
Support for Closure forward composition.
|
void |
run() |
void |
setDelegate(Object delegate)
Allows the delegate to be changed such as when performing markup building
|
void |
setDirective(int directive) |
void |
setProperty(String property,
Object newValue)
Sets the given property to the new value.
|
void |
setResolveStrategy(int resolveStrategy)
Sets the strategy which the closure uses to resolve property references and methods.
|
protected static Object |
throwRuntimeException(Throwable throwable) |
Closure<V> |
trampoline()
Builds a trampolined variant of the current closure.
|
Closure<V> |
trampoline(Object... args)
Builds a trampolined variant of the current closure.
|
getMetaClass, invokeMethod, setMetaClass
public static final int OWNER_FIRST
class Test { def x = 30 def y = 40 def run() { def data = [ x: 10, y: 20 ] def cl = { y = x + y } cl.delegate = data cl() println x println y println data } } new Test().run()will output :
30 70 [x:10, y:20]because the x and y fields declared in the Test class the variables in the delegate.
Note that local variables are always looked up first, independently of the resolution strategy.
public static final int DELEGATE_FIRST
class Test { def x = 30 def y = 40 def run() { def data = [ x: 10, y: 20 ] def cl = { y = x + y } cl.delegate = data cl.resolveStrategy = Closure.DELEGATE_FIRST cl() println x println y println data } } new Test().run()will output :
30 40 [x:10, y:30]because the x and y variables declared in the delegate shadow the fields in the owner class.
Note that local variables are always looked up first, independently of the resolution strategy.
public static final int OWNER_ONLY
class Test { def x = 30 def y = 40 def run() { def data = [ x: 10, y: 20, z: 30 ] def cl = { y = x + y + z } cl.delegate = data cl.resolveStrategy = Closure.OWNER_ONLY cl() println x println y println data } } new Test().run()will throw "No such property: z" error because even if the z variable is declared in the delegate, no lookup is made.
Note that local variables are always looked up first, independently of the resolution strategy.
public static final int DELEGATE_ONLY
class Test { def x = 30 def y = 40 def z = 50 def run() { def data = [ x: 10, y: 20 ] def cl = { y = x + y + z } cl.delegate = data cl.resolveStrategy = Closure.DELEGATE_ONLY cl() println x println y println data } } new Test().run()will throw an error because even if the owner declares a "z" field, the resolution strategy will bypass lookup in the owner.
Note that local variables are always looked up first, independently of the resolution strategy.
public static final int TO_SELF
Note that local variables are always looked up first, independently of the resolution strategy.
public static final int DONE
public static final int SKIP
public static final Closure IDENTITY
protected Class[] parameterTypes
protected int maximumNumberOfParameters
public Closure(Object owner)
owner
- the Closure ownerpublic void setResolveStrategy(int resolveStrategy)
resolveStrategy
- The resolve strategy to setDELEGATE_FIRST
,
DELEGATE_ONLY
,
OWNER_FIRST
,
OWNER_ONLY
,
TO_SELF
public int getResolveStrategy()
DELEGATE_FIRST
,
DELEGATE_ONLY
,
OWNER_FIRST
,
OWNER_ONLY
,
TO_SELF
public Object getThisObject()
public Object getProperty(String property)
GroovyObject
getProperty
in interface GroovyObject
getProperty
in class GroovyObjectSupport
property
- the name of the property of interestpublic void setProperty(String property, Object newValue)
GroovyObject
setProperty
in interface GroovyObject
setProperty
in class GroovyObjectSupport
property
- the name of the property of interestnewValue
- the new value for the propertypublic boolean isCase(Object candidate)
public V call()
public V call(Object arguments)
arguments
- could be a single value or a List of valuespublic Object getOwner()
public Object getDelegate()
public void setDelegate(Object delegate)
delegate
- the new delegatepublic Class[] getParameterTypes()
public int getMaximumNumberOfParameters()
public Closure asWritable()
Object.toString()
in order
to allow rendering the result directly to a String.public Closure<V> curry(Object... arguments)
Typical usage:
def multiply = { a, b -> a * b } def doubler = multiply.curry(2) assert doubler(4) == 8Note: special treatment is given to Closure vararg-style capability. If you curry a vararg parameter, you don't consume the entire vararg array but instead the first parameter of the vararg array as the following example shows:
def a = { one, two, Object[] others -> one + two + others.sum() } assert a.parameterTypes.name == ['java.lang.Object', 'java.lang.Object', '[Ljava.lang.Object;'] assert a(1,2,3,4) == 10 def b = a.curry(1) assert b.parameterTypes.name == ['java.lang.Object', '[Ljava.lang.Object;'] assert b(2,3,4) == 10 def c = b.curry(2) assert c.parameterTypes.name == ['[Ljava.lang.Object;'] assert c(3,4) == 10 def d = c.curry(3) assert d.parameterTypes.name == ['[Ljava.lang.Object;'] assert d(4) == 10 def e = d.curry(4) assert e.parameterTypes.name == ['[Ljava.lang.Object;'] assert e() == 10 assert e(5) == 15
arguments
- the arguments to bindpublic Closure<V> curry(Object argument)
argument
- the argument to bindcurry(Object...)
public Closure<V> rcurry(Object... arguments)
def divide = { a, b -> a / b } def halver = divide.rcurry(2) assert halver(8) == 4
arguments
- the arguments to bindcurry(Object...)
public Closure<V> rcurry(Object argument)
argument
- the argument to bindrcurry(Object...)
public Closure<V> ncurry(int n, Object... arguments)
def caseInsensitive = { a, b -> a.toLowerCase() <=> b.toLowerCase() } as Comparator def caseSensitive = { a, b -> a <=> b } as Comparator def animals1 = ['ant', 'dog', 'BEE'] def animals2 = animals1 + ['Cat'] // curry middle param of this utility method: // Collections#binarySearch(List list, Object key, Comparator c) def catSearcher = Collections.&binarySearch.ncurry(1, "cat") [[animals1, animals2], [caseInsensitive, caseSensitive]].combinations().each{ a, c -> def idx = catSearcher(a.sort(c), c) print a.sort(c).toString().padRight(22) if (idx < 0) println "Not found but would belong in position ${-idx - 1}" else println "Found at index $idx" } // => // [ant, BEE, dog] Not found but would belong in position 2 // [ant, BEE, Cat, dog] Found at index 2 // [BEE, ant, dog] Not found but would belong in position 2 // [BEE, Cat, ant, dog] Not found but would belong in position 3
n
- the index from which to bind parameters (may be -ve in which case it will be normalized)arguments
- the arguments to bindcurry(Object...)
public Closure<V> ncurry(int n, Object argument)
argument
- the argument to bindncurry(int, Object...)
public <W> Closure<W> rightShift(Closure<W> other)
Typical usage:
def twice = { a -> a * 2 } def thrice = { a -> a * 3 } def times6 = twice >> thrice // equivalent: times6 = { a -> thrice(twice(a)) } assert times6(3) == 18
other
- the Closure to compose with the current Closurepublic Closure<V> leftShift(Closure other)
Typical usage:
def twice = { a -> a * 2 } def thrice = { a -> a * 3 } def times6 = thrice << twice // equivalent: times6 = { a -> thrice(twice(a)) } assert times6(3) == 18
other
- the Closure to compose with the current Closurepublic Closure<V> memoize()
public Closure<V> memoizeAtMost(int maxCacheSize)
maxCacheSize
- The maximum size the cache can grow topublic Closure<V> memoizeAtLeast(int protectedCacheSize)
protectedCacheSize
- Number of cached return values to protect from garbage collectionpublic Closure<V> memoizeBetween(int protectedCacheSize, int maxCacheSize)
protectedCacheSize
- Number of cached return values to protect from garbage collectionmaxCacheSize
- The maximum size the cache can grow topublic Closure<V> trampoline(Object... args)
def fact fact = { n, total -> n == 0 ? total : fact.trampoline(n - 1, n * total) }.trampoline() def factorial = { n -> fact(n, 1G)} println factorial(20) // => 2432902008176640000
args
- Parameters to the closure, so as the trampoline mechanism can call itpublic Closure<V> trampoline()
trampoline(Object...)
public int getDirective()
public void setDirective(int directive)
directive
- The directive to set.public Closure<V> dehydrate()
public Closure<V> rehydrate(Object delegate, Object owner, Object thisObject)
dehydrate()
method.delegate
- the closure delegateowner
- the closure ownerthisObject
- the closure "this" object