1) Introduction
In this article, let us look into one of the important features supported in Groovy, the Closures. Groovy is an object-oriented scripting language in which the syntax resembles Java. Not all languages support the concept of Closures directly, although they may provide indirect support, that too with so many limitations and restrictions. This article will make you familiar with the concepts of Closures and will provide considerable amount of code snippets to make the understanding clearer. This article assumes that the reader is having sufficient knowledge in Groovy Programming and first-time readers may look into the Introductory Article on Groovy before proceeding with this article.
2) Closures
To say in simple words, a Closure definition is a block of code that can be re-used any number of times. The definition may look more or less like the definition of a function or a method. A Closure resembles a function or a method in many aspects, however there are subtle differences between them. For example, a Closure may take any number of arguments like a function. A Closure has a return value like a method. A closure can be re-used and referenced any number of times and anywhere similar to a method.
It is clear that a Closure is very similar to a function in many aspects, but the difference is that a Closure can be passed as an argument to a function. A programming language like Java supports similar kind of Closures through anonymous Inner classes. For example, consider the following piece of code,
Button button = new Button();
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent actionEvent)
{
// Do something here.
}
});
In the above code, the method Button.addActionListener() will take an argument of type ActionListener. We have defined something which is of type ActionListener and has included a method actionPerformed() within it. This type cannot be used elsewhere in the code since we don't have any reference to it. One possible way to re-use the reference is to have something like the following,
ActionListener actionListener = new ActionListener(){
public void actionPerformed(ActionEvent actionEvent)
{
// Do something here.
}
};
Button button = new Button();
button.addActionListener(actionListener);
To understand the need to have Closures, let us analyze the situation in this way. You have some simple piece of logic (such as printing the state of an object) and you want this logic to be re-used by some common group of objects. The more traditional way to arrive at the solution for this problem is to define a method with suitable argument list and then to call this method. However, in an object-oriented programming language like Java, methods doesn't exist on their own. They have to be embedded within a class. It doesn't look nice to create a new type (in the form of a class) and to embed a simple behavior in the form of a method. This is where Closures come in. They are simply a named type for some set of statements containing some useful logic. The named type can then be re-used any number of times.
The same problem exists with callbacks. Classes with the design of call-backs in mind, usually have a well-defined interface along with methods. And the client application (or the caller) has to define an implementation class conforming to the interface which often results in defining new types. This will result in the many smaller classes in the Application. One form of solution to these kind of problems is manifested in the form of Closures.
|