Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Kenneth A. Kousen - Making Java Groovy - 2014.pdf
Скачиваний:
50
Добавлен:
19.03.2016
Размер:
15.36 Mб
Скачать

Loops and conditionals

311

last value computed by a closure is returned automatically, both ways return the argument to the closure, which is why it was called echo in the first place.

CLOSURE RETURN VALUES The last evaluated expression in a closure is returned automatically.

If a closure takes more than one argument, or if you don’t want to use the default name, use an arrow to separate the dummy argument names from the body of the closure. Here’s a simple sum, once with the default and once with a named argument:

def total = 0

(1..10).each { num -> total += num } assert (1..10).sum() == total

total = 0

(1..10).each { total += it } assert (1..10).sum() == total

Closures are used throughout this book and fill an entire chapter in GinA. This little amount of information is enough to make a lot of progress.

Returning to the basic constructs of the language, I’ll now show how Groovy differs from Java when using loops and conditional tests.

B.6 Loops and conditionals

In this section, I’ll discuss two features that appear in any programming language: looping through a set of values and making decisions.

B.6.1 Loops

When Groovy was first created, and for some time afterward, it didn’t support the standard Java for loop:

for (int i = 0; i < 5; i++) { ... }

In version 1.6, however, the core committers decided that it was more important to support Java constructs than to try to keep the language free of that somewhat awkward syntax that Java inherited from its predecessors. Many demonstrations of Groovy start with a Java class, rename it with a .groovy extension, and show that it still compiles successfully with the Groovy compiler. The result is far from idiomatic Groovy, but it does illustrate a valid point: Groovy is the closest to Java of the new family of JVM languages.

JAVA LOOPS Groovy supports the standard Java for loop and for-each loop, as well as the while loop. It does not, however, support the do-while construct.

The for-each loop in Java was introduced in Java SE 1.5 and works for any linear collection, including both arrays and lists:

for (String s : strings) { ... }

www.it-ebooks.info

312

APPENDIX B Groovy by feature

The for-each loop is helpful, because it means you don’t always need to get an iterator to loop over the elements of a list. The price you pay is that there’s no explicit index. Inside the loop, you know what element you’re currently on, but not where it appears in the list. If you need to know the index, you can either keep track of the index yourself or go back to the traditional for loop.

Groovy supplies a variation on the for-each loop that avoids the colon syntax, called a for-in loop:

def words = "I'm a Groovy coder".tokenize() def capitalized = ''

for (word in words) {

capitalized += word.capitalize() + ' '

}

assert capitalized == "I'm A Groovy Coder "

Note that unlike the for-each loop, the value variable is not declared to have a type: not even def.

Still, none of those loops are the most common way of iterating in Groovy. Rather than write an explicit loop, as in the previous examples, Groovy prefers a more direct implementation of the Iterator design pattern. Groovy adds the each method, which takes a closure as an argument, to collections. The each method then applies the closure to each element of the collection:

(0..5).each { println it }

Again, because the closure is the last argument of the method, it can be placed after the parentheses. Because there are no other arguments to the each method, the parentheses can be eliminated entirely.

EACH The each method is the most common looping construct in Groovy.

The Iterator design pattern recommends separating the way you walk through the elements of a collection from what you plan to do with those elements. The each method does the iterating internally. The user determines what to do with the elements by supplying a closure, as shown. Here the closure prints its argument. The each method supplies each value in the range, one by one, to the closure, so the result is to print the numbers from zero to five.

Like the for-in loop, inside the closure you have access to each element, but not to the index. If you want the index, though, there’s an additional method available called eachWithIndex:

def strings = ['how','are','you'] def results = []

strings.eachWithIndex { s,i -> results << "$i:$s" } assert results == ['0:how', '1:are', '2:you']

The closure supplied to the eachWithIndex method takes two dummy arguments. The first is the value from the collection, and the second is the index.

www.it-ebooks.info

Loops and conditionals

313

I should mention that although all these loops work correctly, there can be differences in how much time each of them takes. If you’re dealing with a collection of a few dozen elements or less, the differences will probably not be noticeable. If the number of iterations is going to be in the tens of thousands or more, you probably should profile the resulting code.

B.6.2 Conditionals

Java has two types of conditional statements: the if statement and its related constructs, like if-else and switch statements. Both are supported by Groovy. The if statement works pretty much the same way it does in Java. The switch statement, however, has been taken from Java’s crippled form and restored to its former glory.

Groovy’s version of the if statement is similar to Java’s, with the difference being the so-called Groovy Truth. In Java, the argument to an if statement must be a Boolean expression, or the statement won’t compile. In Groovy, lots of things evaluate to true other than Boolean expressions.

For example, nonzero numbers are true:

if (1) {

assert true } else {

assert false

}

The result is true. This expression wouldn’t work in Java. There you would have to compare the argument to another value, resulting in a Boolean expression.

Return to C?

The Groovy Truth is a case where Java restricted something C supported (nonBoolean expressions in decision statements), but Groovy brought it back. That can certainly lead to bugs that Java would avoid.

From a philosophical point of view, why do it? By restricting what was allowed, Java made certain types of bugs much less likely. Groovy, by restoring those features, increases the possibility of those bugs again. Is the gain worth it?

My opinion is that this is a side effect of the increased emphasis on testing that has swept through the development community. If you’re going to have to write tests to prove your code is correct anyway, why not take advantage of the greater power? Sure, you’ve introduced the possibility of getting some bugs past the compiler, but just because it compiles doesn’t mean it’s right. The tests prove correctness, so why not use shorter, more powerful code when you can?

Returning to decision statements, Java also supports a ternary operator, and Groovy does the same:

String result = 5 > 3 ? 'x' : 'y' assert result == 'x'

www.it-ebooks.info

314

APPENDIX B Groovy by feature

The ternary expression reads, is five greater than three? If so, assign the result to x, otherwise use y. It’s like an if statement, but shorter.

There’s a reduced form of the ternary operator that highlights both Groovy’s helpfulness and its sense of humor: the Elvis operator.

B.6.3 Elvis

Consider the following use case. You’re planning to use an input value, but it’s optional. If the client supplies it, you’ll use it. If not, you plan to use a default instead.

I’ll use a variable called name as an example:

String displayName = name ? name : 'default'

This means if name is not null, use it for displayName. Otherwise, use a default. I’m using a standard ternary operator to check whether name is null or not. The way this is written has some repetition in it. After all, I want to use name if it’s available, so why do I have to repeat myself?

That’s where the Elvis operator comes in. Here’s the revised code:

String displayName = name ?: 'default'

The Elvis operator is the combination of a question mark and a colon formed by leaving out the value in between them in the ternary operator. The idea is that if the variable in front of the question mark is not null, use it. The ?: operator is called Elvis because if you turn your head to the side, the result looks vaguely like the King:

def greet(name) { "${name ?: 'Elvis'} has left the building" } assert greet(null) == 'Elvis has left the building'

assert greet('Priscilla') == 'Priscilla has left the building'

The greet method takes a parameter called name and uses the Elvis operator to determine what to return. This way it still has a reasonable value, even if the input argument is null.15

B.6.4 Safe de-reference

There’s one final conditional operator that Groovy provides that saves many lines of coding. It’s called the safe de-reference operator, written as ?..

The idea is to avoid having to constantly check for nulls. For example, suppose you have classes called Employee, Department, and Location. If each employee instance has a department, and each department has a location, then if you want the location for an employee, you would write something like this (in Java):

Location loc = employee.getDepartment().getLocation()

But what happens if the employee reference is null? Or what happens if the employee hasn’t been assigned a department, so the getDepartment method returns null? Those possibilities mean the code expands to

15 Thank you, thank you very much.

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]