程式扎記: [ In Action ] The Groovy basics - Groovy at a glance

標籤

2013年12月24日 星期二

[ In Action ] The Groovy basics - Groovy at a glance

Preface: 
Take this section as a broad but shallow overview. It won’t answer all your questions, but it will enable you to start experiencing Groovy on your own. We encourage you to experiment—if you wonder what would happen if you were to tweak the code in a certain way, try it! You learn best by experience. We promise to give detailed explanations in later in-depth chapters. 

Declaring classes: 
Classes are the cornerstone of object-oriented programming, because they define the blueprint from which objects are drawn. Listing 2.2 contains a simple Groovy class named Book , which has an instance variable title , a constructor that sets the title, and a getter method for the title. Note that everything looks much like Java, except there’s no accessibility modifier: Methods are public by default
- Listing 2.2 A simple Book class 
  1. package inaction  
  2.   
  3. class Book {  
  4.     private String title  
  5.   
  6.     Book (String theTitle) {title = theTitle}  
  7.       
  8.     String getTitle(){return title}  
  9. }  
The code is not surprising. Class declarations look much the same in most object-oriented languages. The details and nuts and bolts of class declarations will be explained in chapter 7. Next we are going to use groovy as scripts which are text files, typically with an extension of .groovy, that can be executed from the command shell via: 
> groovy myfile.groovy

Note that this is very different from Java. In Groovy, we are executing the source code! An ordinary Java class is generated for us and executed behind the scenes. But from a user’s perspective, it looks like we are executing plain Groovy source code. 

Scripts contain Groovy statements without an enclosing class declaration. Scripts can even contain method definitions outside of class definitions to better structure the code. You will learn more about scripts in chapter 7. Until then, take them for granted. Listing 2.3 shows how easy it is to use the Book class in a script. We create a new instance and call the getter method on the object by using Java’s dot-syntax. Then we define a method to read the title backward. 
- Listing 2.3 Using the Book class from a script 
  1. import inaction.Book  
  2.   
  3.   
  4. Book gina = new Book('Groovy in Action')  
  5. assert gina.getTitle()         == 'Groovy in Action'  
  6. assert getTitleBackwards(gina) == 'noitcA ni yvoorG'  
  7.   
  8. String getTitleBackwards(book) {  
  9.     def title = book.getTitle()  
  10.     return title.reverse()  
  11. }  
Note how we are able to invoke the method getTitleBackwards before it is declared. Behind this observation is a fundamental difference between Groovy and other scripting languages such as Ruby. A Groovy script is fully constructed—that is, parsed, compiled, and generated—before execution. Section 7.2 has more details about this. 

Another important observation is that we can use Book objects without explicitly compiling the Book class! The only prerequisite for using the Book class is thatBook.groovy must reside on the classpath. The Groovy runtime system will find the file, compile it transparently into a class, and yield a new Book object. Groovy combines the ease of scripting with the merits of object orientation. 

This inevitably leads to how to organize larger script-based applications. In Groovy, the preferred way is not meshing together numerous script files, but instead grouping reusable components in classes such as Book . Remember that such a class remains fully scriptable; you can modify Groovy code, and the changes are instantly available without further action. Programming the Book class and the script that uses it was simple. It’s hard to believe that it can be any simpler, but it can, as you will see next. 

GroovyBeans: 
GroovyBean is a JavaBean defined in Groovy. In Groovy, working with beans is much easier than in Java. Groovy facilitates working with beans in three ways: 
■ Generating the accessor methods
■ Allowing simplified access to all JavaBeans (including GroovyBeans)
■ Simplified registration of event handlers

Listing 2.4 shows how our Book class boils down to a one-liner defining the title property. This results in the accessor methods getTitle() and setTitle(title) being generated. We also demonstrate how to access the bean the standard way with accessor methods, as well as the simplified way, where property access reads like direct field access. 
- Listing 2.4 Defining the Book class as a GroovyBean 
  1. class Book{  
  2.     String title  
  3. }  
  4.   
  5. def gBook = new Book()  
  6. gBook.setTitle("Groovy conquers the world")  
  7. assert gBook.getTitle() == "Groovy conquers the world"  
  8.   
  9. gBook.title = "Groovy in Action"  
  10. assert gBook.title == "Groovy in Action"  
Note that groovyBook.title is not a field access. Instead it is a shortcut for the corresponding accessor method. More information about methods and beans will be given in chapter 7. 

Handling text: 
Just like in Java, character data is mostly handled using the java.lang.String class. However, Groovy provides some tweaks to make that easier, with more options for string literals and some helpful operators. 

GStrings 
In Groovy, string literals can appear in single or double quotes. The double-quoted version allows the use of placeholders, which are automatically resolved as required. This is a GString, and that’s also the name of the class involved. The following code demonstrates a simple variable expansion, although that’s not all GString can do: 
  1. def nick = 'Gina'  
  2. def book = 'Groovy in Action'  
  3. assert "$nick is $book" == 'Gina is Groovy in Action'  
Chapter 3 provides more information about strings, including more options for GStrings, how to escape special characters, how to span string declarations over multiple lines, and available methods and operators on strings. As you’d expect, GStrings are pretty neat. 

Regular expressions 
If you are familiar with the concept of regular expressions, you will be glad to hear that Groovy supports them at the language level. You will find a full introduction to the topic in chapter 3. Groovy provides a means for easy declaration of regular expression patterns as well as operators for applying them. Figure 2.2 declares a pattern with the slashy // syntax and uses the =~ find operator to match the pattern against a given string. The first line ensures that the string contains a series of digits; the second line replaces every digit with an x. 
 
Figure 2.2 Regular expression support in Groovy through operators and slashy strings 

Chapter 3 explains how to declare and use regular expressions and goes through the ways to apply them. 

Numbers are objects: 
Groovy numbers have a familiar appearance, but unlike in Java, they are first-class objects, not primitive types. In Java, you cannot invoke methods on primitive types. If xis of primitive type int , you cannot write x.toString() . On the other hand, if y is an object, you can-not use 2*y. In Groovy, both are possible. You can use numbers with numeric operators, and you can also call methods on number instances. 
  1. def x = 1  
  2. def y = 2  
  3. assert x + y == 3  
  4. assert x.plus(y) == 3  
  5. assert x instanceof Integer  
The variables x and y are objects of type java.lang.Integer. Thus, we can use the plus method. But we can just as easily use the + operator. 

This is surprising and a major lift to object orientation on the Java platform. Whereas Java has a small but ubiquitously used part of the language that isn’t object-oriented at all, Groovy makes a point of using objects for everything. You will learn more about how Groovy handles numbers in chapter 3 

Using lists, maps, and ranges: 
Groovy makes collection handling simple, with added support for operators, literals, and extra methods beyond those provided by the Java standard libraries. 

Lists 
Java supports indexing arrays with a square bracket syntax, which we will call the subscript operator. Groovy allows the same syntax to be used with lists—instances ofjava.util.List —which allows adding and removing elements, changing the size of the list at runtime, and storing items that are not necessarily of a uniform type. In addition, Groovy allows lists to be indexed outside their current bounds, which again can change the size of the list. Further-more, lists can be specified as literals directly in your code. 

The list is constructed such that each index matches its representation as a Roman numeral. Working with the list looks much like working with arrays, but in Groovy, the manipulation is more expressive, and the restrictions that apply to arrays are gone: 
  1. def roman = ['''I''II''III''IV''V''VI''VII']     
  2. assert roman[4] == 'IV'   // List access  
  3. roman[8] = 'VIII'              // List expansion  
  4. assert roman.size() ==   
Later, in section 4.2, we will discuss more capabilities of the list datatype. 

Simple maps 
Unlike Java, Groovy supports maps at the language level, allowing them to be specified with literals and providing suitable operators to work with them. It does so with a clear and easy syntax. The syntax for maps looks like an array of key-value pairs, where a colon separates keys and values. That's all it takes. 

The following example stores descriptions of HTTP return codes in a map, as depicted in figure 2.4: 
 
Figure 2.4 An example map where HTTP return codes map to their respective messages 

You see the map declaration and initialization, the retrieval of values, and the addition of a new entry. All of this is done with a single method call explicitly appearing in the source code—and even that is only checking the new size of the map: 
  1. def http = [  
  2.     100 : 'CONTINUE',  
  3.     200 : 'OK',    
  4.     400 : 'BAD REQUEST'   ]  
  5. assert http[200] == 'OK'  
  6. http[500] = 'INTERNAL SERVER ERROR'  
  7. assert http.size() == 4  
Section 4.3 gives more information about maps and the wealth of their Groovy feature set. 

Ranges 
Although ranges don’t appear in the standard Java libraries, most programmers have an intuitive idea of what a range is—effectively a start point and an end point, with a notion of how to move from the start to the end point. Again, Groovy provides literals to support this useful concept, along with other language features such as the forstatement, which understands ranges. 

The following code demonstrates the range literal format, along with how to find the size of a range, determine whether it contains a particular value, find its start and end points, and reverse it: 
  1. def x  = 1..10  
  2. assert x.contains(5)  
  3. assert x.contains(15) == false  
  4. assert x.size()    == 10  
  5. assert x.from      == 1  
  6. assert x.to        == 10  
  7. assert x.reverse() == 10..1  
Code as objects: closures 
The concept of closures is not a new one, but it has usually been associated with functional languages, allowing one piece of code to execute an arbitrary piece of code that has been specified elsewhere. Groovy allows closures to be specified inline in a concise, clean, and powerful way, effectively promoting the Method-Object pattern to a first-class position in the language. 

Because closures are a new concept to most Java programmers, it may take a little time to adjust. The good news is that the initial steps of using closures are so easy that you hardly notice what is so new about them. The aha-wow-cool effect comes later, when you discover their real power. 

Informally, a closure can be recognized as a list of statements within curly braces, like any other code block. It optionally has a list of identifiers in order to name the parameters passed to it, with an -> arrow marking the end of the list. It’s easiest to understand closures through examples. Figure 2.5 shows a simple closure that is passed to the List.each method, called on a list [1, 2, 3] . 
 
Figure 2.5 A simple example of a closure that prints the numbers 1, 2 and 3 

The List.each method takes a single parameter—a closure. It then executes that closure for each of the elements in the list, passing in that element as the argument to the closure. In this example, the main body of the closure is a statement to print out whatever is passed to the closure, namely the parameter we've called entry

Let’s consider a slightly more complicated question: If n people are at a party and everyone clinks glasses with everybody else, how many clinks do you hear? Figure 2.6 sketches this question for five people, where each line represents one clink. 
 
Figure 2.6 Five elements and their distinct connections, modeling five people (the circles) at a party clinking glasses with each other (the lines). Here there are 10 “clinks.” 

To answer this question, we can use Integer ’s upto method, which does something for every Integer starting at the current value and going up to a given end value. We apply this method to the problem by imagining people arriving at the party one by one. As people arrive, they clink glasses with everyone who is already present. This way, everyone clinks glasses with everyone else exactly once. 

Listing 2.5 shows the code required to calculate the number of clinks. We keep a running total of the number of clinks, and when each guest arrives, we add the number of people already present (the guest number – 1). Finally, we test the result using Gauss’s formula 5 for this problem—with 100 people, there should be 4,950 clinks. 
- Listing 2.5 Counting all the clinks at a party using a closure 
  1. def totalClinks = 0  
  2. def partyPeople = 100  
  3. 1.upto(partyPeople) { guestNumber ->  
  4.     clinksWithGuest = guestNumber-1  
  5.     totalClinks += clinksWithGuest  
  6. }  
  7. println "Total Clinks=$totalClinks"  
  8. assert totalClinks == (partyPeople*(partyPeople-1))/2  
Groovy control structures: 
In conditionals, null is treated like false; not-null is treated like true. The for loop has a for(i in x){body} notation, where x can be anything that Groovy knows how to iterate through, such as an iterator, an enumeration, a collection, a range, a map, or literally any object, as explained in chapter 6. In Groovy, the for loop is often replaced by iteration methods that take a closure argument. Listing 2.6 gives an overview. 
- Listing 2.6 Control structures 
  1. if (falseassert false  
  2. if (null)  
  3. {  
  4.     assert false  
  5. }  
  6. else  
  7. {  
  8.     assert true  
  9. }  
  10. def i = 0  
  11. while (i < 10) {  
  12.     i++  
  13. }                  
  14. assert i == 10   
  15.   
  16. def clinks = 0  
  17. for (remainingGuests in 0..9) {  
  18.     clinks += remainingGuests  
  19. }  
  20. assert clinks == (10*9)/2  
  21.   
  22. def list = [0123456789]  
  23. for (j in list) {  
  24.     assert j == list[j]  
  25. }  
  26. list.each() { item ->  
  27.     assert item == list[item]  
  28. }  
  29. switch(3)  {  
  30.     case 1 : assert falsebreak  
  31.     case 3 : assert true;  break  
  32.     defaultassert false  
  33. }  
Supplement: 
Groovy Examples: Create a simple loop

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!