Monday, February 12, 2018

Groovy: Advanced Concepts: Sequencers

In addition to the sequencers and control flow elements described in Control Flow, Groovy also implements the following additional sequencers: jumps, escapes and exceptions.

Jumps

Java implements limited jump capabilities, limited to the break and continue escapes, as described in the next section.

Escapes

The Groovy language supports the following escape and branching statements: break, continue and return.  Each of these allow for escaping or modifying the next step in a loop or function.

break

Within a loop or switch, a break instructs the runtime to exit that composite statement.  This is useful when a loop needs to be terminated without meeting its completion criteria or when a particular case statement is complete.
while(true)
{
    // Break when the first true is returned
    if(new Random().nextBoolean())
        break;
}
 
switch(1)
{
    case 1:
        // Do something and then exit
        break;
    case 2:
    case 3:
        // Case 2 falls through to case 3
    default:
        // Case 2 and 3 will fall through
}
Breaks can be both labeled and unlabeled, implementing a limited form of jump.  Unlabeled break statements will terminate the innermost statement, while a labeled break statement will break labeled statement. [10]  The following example will break the outermost for loop when i is greater than 5 and when i + j equals 17.
outermost:
for(int i = 0; i < 10; i++)
{
    for(int j = 0; j < 10; j++)
    {
        if(i < 5 && i + j == 17)
            break;
   
        if(i + j == 17)
            break outermost
           
        println "${i},${j} = ${i+j}"
    }
}

continue

Within a loop, a continue statement will cause the processing of loop commands to stop and control be returned to the top of the loop.  The following example will only print the odd numbers as even numbers will satisfy the if statement, triggering the continue.
for(int i = 0; i < 10; i++)
{
    if(i % 2 == 0)
        continue;
       
    println i
}
In the same way as break statements, continue statements can be labeled or unlabeled.  Continues will follow the same rules for which loop to target if labeled or unlabeled.

return

The final flow control statement supported by Groovy is the return statement.  Within a function or method, a return statement will immediately exit the function and, if provided, return the value defined in the return statement.  The value returned must be of a compatible type to the defined return value for the function.
Groovy adds an additional consideration in that, unlike Java, Groovy will assume the output of the last operation will be returned at the end of a function.  If the final statement does not return a value, Groovy will attempt to return a value of null.  This can result in class cast exceptions if care is not taken to ensure all function exit points return the appropriate data type because the compiler may not catch these edge cases.

Exceptions

Groovy offers exception handling like that of Java.  In the instance of a programming error, unintended condition or other unrecoverable error, a method or operation can throw an exception.  This exception is an object that is ultimately a subclass of java.lang.Exception.  This object includes the ability to include a descriptive message and the initial cause of the exception.
Groovy exceptions are implemented by wrapping a function call, or set of function calls, in a try block.  The try block is followed by one or more catch blocks.  Each catch block defines one or more exception classes and a collection of statements to be executed in the event of an exception of that type.  A try/catch block is optionally followed by a finally block.  The code in the finally block is executed regardless of whether an exception is thrown or not and is often used to clean up resources.
try
{
}
catch(ExceptionTypeOne e1)
{
    // code do deal with ExceptionTypeOne and any subclasses
}
catch(ExceptionTypeTwo e2)
{
    // code do deal with ExceptionTypeTwo and any subclasses
}
finally
{
    // code to be executed at the end of the block regardless
    // of outcome (successful or exceptional completion)
}
There are two types of exceptions: a base Exception (a checked exception) and a RuntimeException (an unchecked exception).  While both can be used to indicate that an error has occurred and that the developer should take corrective action, RuntimeExceptions do not need to be dealt with in code.  The Groovy language requires that exceptions that are not declared as RuntimeException must either be caught or declared in the method definition.
// The following method throws a FileNotFoundException
void method() throws FileNotFoundException
{
    // The following call throws a FileNotFoundException
    // and must be declared or caught
    FileInputStream fis = new FileInputStream(“foo”)
 
    // Attempt to read data from fis
    try
    {
         // throws an IO exception, caught below
         int byte = fis.read()
    }
    catch(IOException e)
    {
         // Rethrow the exception as a runtime exception
         // which does not need to be declared
         throw new RuntimeException(“Failed reading”)
    }
    finally
    {
         try
         {
          fis.close()
     }
     catch(IOException ex)
     {
          throw new RuntimeException(ex)
     }
    }
}

No comments: