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

298

APPENDIX B Groovy by feature

B.2 Variables, numbers, and strings

Groovy is an optionally typed language. Groovy uses classes to define data types, just as Java does, but Groovy variables can either have a static type or use the def keyword.

For example, I’m perfectly free to declare variables of type int, String, or Employee, using the standard Java syntax:

int x String name

Employee fred

If I don’t know the type of the variable, or I don’t care, Groovy provides the keyword def:

def arg

Typed vs. untyped variables

When should you use def as opposed to the actual type? There’s no strict answer, but recently I had a (very mild) Twitter debate about this issue with Dierk Koenig (lead author of GinA), Andres Almiray (lead author of Griffon in Action and head of the Griffon project), and Dave Klein (lead author of Grails: A Quick-Start Guide). Dierk had the best recommendation I’ve ever heard on the subject. He said, “If I think of a type, I type it (pun intended).”

My own experience is that as I get more experienced with Groovy, I tend to use def less and less. I agree with Dierk’s recommendation, with the added advice that now when I declare a type, I pause for a moment to see if any actual type occurs to me. If so, I use it.

In some cases def is preferred, most notably when using mock objects in testing. That subject is discussed in chapter 6.

Moving on to data types themselves, Java makes a distinction between primitives and classes. In Groovy there are no primitives. Numbers in Groovy are first-class objects, with their own set of methods.

B.2.1 Numbers

Because in Groovy numbers are objects, I can determine their data types. For integer literals, the data type depends on the value, as shown in this script:

x = 1

assert x.class == java.lang.Integer x = 10000000000000000

assert x.class == java.lang.Long x = 100000000000000000000000

assert x.class == java.math.BigInteger

There are a few points to be made about this script. First, the variable x doesn’t have a declaration at all. This is only legal in a script, where the variable becomes part of the script’s binding and can be set and accessed from outside. Details of this procedure

www.it-ebooks.info

Variables, numbers, and strings

299

are shown in chapter 3 on integration with Java. Suffice it to say here that this is legal in a script, but not in a class. If it makes you feel more comfortable, you’re free to add the word def in front of x.

SCRIPT VARIABLES If a variable in a script is not declared, it becomes part of the script’s binding.

As mentioned earlier, the script lacks semicolons. Semicolons as statement separators are optional in Groovy and can be omitted if there’s no ambiguity. Again, you’re free to add them in without a problem.

SEMICOLONS In Groovy, semicolons work but are optional.

Next, Groovy uses the method called assert extensively. The word assert can be written without parentheses, as done here, or you can surround an expression with them. The resulting expression must evaluate to a Boolean, but that’s a much looser requirement than in Java. In Java, the only available Booleans are true and false. In Groovy, non-null references are true, as are nonzero numbers, non-empty collections, nonempty strings, and the Boolean value true.

That bears repeating and goes by the term The Groovy Truth.

THE GROOVY TRUTH In Groovy, non-null references, non-empty collections, non-empty strings, nonzero numbers, and the Boolean value true are all true.

Finally, the default data type for floating-point values in Java is double, but in Groovy it’s java.math.BigDecimal. The double type in Java has approximately 17 decimal places of precision, but if you want to get depressed about its accuracy, try this tiny sample:

println 2.0d – 1.1d

The d appended to the literals makes them doubles. You would expect the answer here to be 0.9, but in fact it’s 0.8999999999999999. That’s not much of a difference, but I’ve only done a single subtraction and I’m already off. That’s not good. That’s why any serious numerical calculations in Java require java.math.BigDecimal, but that means you can’t use the standard operators (+, -, *, /) anymore and have to use method calls instead.

Groovy handles that issue without a problem. Here’s the analogous Groovy script:

println 2.0 – 1.1

The answer in this case is 0.9, as expected. Because the calculations are done with BigDecimal, the answer is correct. Groovy also has operator overloading, so the plus operator can be used with the BigDecimal values. To summarize:

LITERALS Numbers without a decimal point are of type Integer, Long, or java.math.BigInteger, depending on size. Numbers with a decimal point are of type java.math.BigDecimal.

www.it-ebooks.info

300

APPENDIX B Groovy by feature

Because numbers are objects, they have methods as well. Listing B.2 shows a script putting some numbers through their paces. Several of the expressions use closures, which are the subject of section B.4. The simplest definition is to consider them a block of code that’s executed as though it’s an anonymous method call.

Listing B.2 numbers.groovy, showing method calls on numeric literals

assert 2**3 == 8

assert 2**-2 == 0.25 // i.e., 1/(2*2) = 1/4

def x = ""

3.times { x += "Hello" } assert x == "HelloHelloHello"

def total = 0

1.upto(3) { total += it } assert total == 1 + 2 + 3

def countDown = []

 

 

 

 

 

5.downto 1, { countDown << "$it ...

" }

 

 

 

assert countDown == ['5 ...

', '4 ...

', '3 ...

', '2 ...

', '1 ...

']

Groovy has an exponentiation operator, unlike Java. Numbers have methods like times, upto, and downto. The times operation takes a single argument of type Closure. When the last argument to a method is a closure, you can put it after the parentheses. Because the method has no other arguments, you can leave out the parentheses altogether.

CLOSURE ARGUMENTS If the last argument to a method is a closure, it can be placed after the parentheses.

The upto and downto methods take two arguments, so the parentheses are shown in the former and a comma is used in the latter to indicate that both the number and the closure are arguments to the method. The countDown variable is a list, which will be discussed in section B.3. The left-shift operator has been overloaded to append to the collection, and its argument here is a parameterized string. Groovy has two types of strings, discussed in the next section.

B.2.2 Strings and Groovy strings

In Java, single quotes delimit characters (a primitive) and double quotes surround instances of java.lang.String. In Groovy, both single and double quotes are used for strings, but there’s a difference. Double-quoted strings are used for parameter replacement. They’re not instances of java.lang.String, but rather instances of

groovy.lang.GString.

Here are a couple of examples to show how they’re used:

def s = 'this is a string'

assert s.class == java.lang.String

def gs = "this might be a GString" assert gs.class == java.lang.String assert !(gs instanceof GString)

www.it-ebooks.info

Variables, numbers, and strings

301

gs = "If I put in a placeholder, this really is a GString: ${1+1}" assert gs instanceof GString

Single-quoted strings are always instances of java.lang.String. Double-quoted strings may or may not be Groovy strings, depending on whether parameter replacement is done or not.

Groovy also has multiline strings, with either single or double quotes. The difference again is whether or not parameter replacement is done:

def picard = '''

(to the tune of Let It Snow)

Oh the vacuum outside is endless Unforgiving, cold, and friendless But still we must boldly go

Make it so, make it so, make it so!

'''

def quote = """

There are ${Integer.toBinaryString(2)} kinds of people in the world: Those who know binary, and those who don't

"""

assert quote == '''

There are 10 kinds of people in the world: Those who know binary, and those who don't

'''

There’s one final kind of string, used for regular expressions. Java has had regularexpression capabilities since version 1.4, but most developers either aren’t aware of them or avoid them.6 One particularly annoying part of regular expressions in Java is that the backslash character, \, is used as an escape character, but if you want to use it in a regular expression, you have to backslash the backslash. This leads to annoying expressions where you have to double-backslash the backslashes, making the resulting expressions almost unreadable.

Groovy provides what’s called the slashy syntax. If you surround an expression with forward slashes, it’s assumed to be a regular expression, and you don’t have to doublebackslash anymore.

STRINGS Groovy uses single quotes for regular strings, double quotes for parameterized strings, and forward slashes for regular expressions.

Here’s an example that checks strings to see if they are palindromes: that is, if they are the same forward and backward. To check for palindromes you first need to remove any punctuation and ignore case before reversing the string:

def palindromes = '''

Able was I ere I saw Elba Madam, in Eden, I'm Adam

6Perl programmers love regular expressions. Ruby developers are fond of them, but reasonable about it. Java developers take one look at the JavaDocs for the java.util.regex.Pattern class and recoil in horror.

www.it-ebooks.info

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