程式扎記: [ In Action ] Groovy control structures - The Groovy truth

標籤

2014年2月14日 星期五

[ In Action ] Groovy control structures - The Groovy truth

Preface: 
In order to understand how Groovy will handle control structures such as if and while , you need to know how it evaluates expressions, which need to have Boolean results. Many of the control structures we examine in this chapter rely on the result of a Boolean test—an expression that is first evaluated and then considered as being either true or false. The outcome of this affects which path is then followed in the code. In Java, the consideration involved is usually trivial, because Java requires the expression to be one resulting in the primitive boolean type to start with. Groovy is more relaxed about this, allowing simpler code at the slight expense of language simplicity. We’ll examine Groovy’s rules for Boolean tests and give some advice to avoid falling into an age-old trap

Evaluating Boolean tests: 
The expression of a Boolean test can be of any (non-void) type. It can apply to any object. Groovy decides whether to consider the expression as being true or false by applying the rules shown in table 6.1, based on the result’s runtime type. The rules are applied in the order given, and once a rule matches, it completely determines the result. 
 

Listing 6.1 shows these rules in action, using the Boolean negation operator ! to assert that expressions which ought to evaluate to false really do so. 
- Listing 6.1 Example Boolean test evaluations 
  1. // Boolean values are trivial  
  2. assert true       
  3. assert !false     
  4.   
  5. // Matchers must match  
  6. assert ('a' =~ /./)      
  7. assert !('a' =~ /b/)  
  8.      
  9. // Collections must be non-empty  
  10. assert [1]     
  11. assert ![]     
  12.   
  13. // Maps must be non-empty  
  14. assert ['a':1]     
  15. assert ![:]       
  16.   
  17. // Strings must be non-empty   
  18. assert 'a'     
  19. assert !''     
  20.   
  21. // Numbers (any type) must be nonzero  
  22. assert 1        
  23. assert 1.1      
  24. assert 1.2f     
  25. assert 1.3g     
  26. assert 2L       
  27. assert 3G       
  28. assert !0       
  29.   
  30. // Any other value must be non-null  
  31. assert new Object()     
  32. assert !null    
These rules can make testing for “truth” simpler and easier to read. However, they come with a price, as you’re about to find out. 

Assignments within Boolean tests: 
Before we get into the meat of the chapter, we have a warning to point out. Just like Java, Groovy allows the expression used for a Boolean test to be an assignment—and the value of an assignment expression is the value assigned. Unlike Java, the type of a Boolean test is not restricted to boolean, which means that a problem you might have thought was ancient history reappears, albeit in an alleviated manner. Namely, an equality operator == incorrectly entered as an assignment operator = is valid code with a drastically different effect than the intended one. Groovy shields you from falling into this trap for the most common appearance of this error: when it’s used as a top-level expression in an if statement. However, it can still arise in less usual cases. 

Listing 6.2 leads you through some typical variations of this topic. 
- Listing 6.2 What happens when == is mistyped as = 
  1. def x = 1                
  2. if (x == 2) {     
  3.     assert false  
  4. }                        
  5. /******************* 
  6. if (x =  2) {    
  7.    println x 
  8. }                       
  9. ********************/  
  10. if ((x = 3)) {     
  11.     println x   
  12. }              
  13. assert x == 3      
  14.         
  15. def store = []        
  16. while (x = x - 1) {     
  17.     store << x  
  18. }  
  19. assert store == [21]  
  20. while (x = 1) {     
  21.     println x     
  22.     break  
  23. }  
This potential cause of bugs has given rise to the idiom in other languages (such as C and C++, which suffer from the same problem to a worse degree) of putting constants on the left side of the equality operator when you wish to perform a comparison with one. This would lead to the last while statement in the previous listing (still with a typo) being: 
  1. while (1 =  x) {   // Should be ==  
  2.     println x  
  3. }  
This would raise an error, as you can’t assign a value to a constant. We’re back to safety—so long as constants are involved. Unfortunately, not only does this fail when both sides of the comparison are variables, it also reduces readability. Whether it is a natural occurrence, a quirk of human languages, or conditioning, most people findwhile(x==3) significantly simpler to read than while(3==x) . Although neither is going to cause confusion, the latter tends to slow people down or interrupt their train of thought. In this book, we have favored readability over safety—but our situation is somewhat different than that of normal development. You will have to decide for yourself which convention suits you and your team better.

沒有留言:

張貼留言

網誌存檔

關於我自己

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