Go for Java Programmers: Control Structures

Go for Java Programmers: Control Structures

Part of the Go for Java Programmers series

Go (Golang) has only three basic control structures.  However, each of them are significantly more versatile than their Java counterparts.

If

The if statement should be immediately recognizable.  In its basic form it appears the same as in Java, just without parentheses around the condition:

...
if err != nil {
   fmt.Println("An error occurred!")
}
...

However, Go’s version differs from Java’s if in two regards.  First, when the body of an if statement is only a single line, Java allows you to omit the opening and closing braces:

...
if(inputString == null) return "null";
...

Go, on the other hand, has some strong opinions on well-formatted and readable code (more on this to come in the next article).  A Go if statement requires an opening and closing brace no matter what:

...
if inputString == null {
   return "null"
}
...

Secondly, Go makes up for this restriction with a flexible initialization feature. Inside of the if declaration itself, you can place an initialization statement just before the condition:

...
if err := os.Remove("data.csv"); err != nil {
   fmt.Println("Unable to delete the file 'data.csv'")
}
...

Here, the if statement performs a file delete operation, returning an error variable that’s populated if the delete failed.  The condition then checks this value, and enters the if block if the condition is true.

Why do it this way, as opposed to simply performing the file delete operation on a separate line above the if statement?  The key feature of this approach is that variables created in an initialization statement have scope limited to the if block.

The example snippet above is a very common pattern in Go, and a later article in this series will deal with error handling more thoroughly.  However, the point is that this initialization construct can create any variable that you need to have in-scope prior to the if condition, yet out-of-scope after the if block.

for

A basic for loop in Go is likewise similar to its Java counterpart.  It contains an initialization statement, a condition to be checked before each iteration, and code to executed following each iteration.  The only difference is that there are no paretheses surrounding these three things:

...
for index := 0; index < 10; index++ {
   fmt.Printf("index == %d", index)
}
...

However the for loop has two other forms besides this basic version.  Go does NOT have the while or do loop control structures found in Java, so the other for loop forms cover the same functionality in their place.

If you write a for statement with only a condition, and no initialization or post-iteration code, then it is essentially a Java while loop:

...
for len(slice) < maxSize {
   slice = append(slice, getNextItemToAdd())
}
...

Of course, you could do something similar with a Java for loop, by inserting empty statements for the initialization and/or post-iteration parts.  However, the Java version always requires three parts, strictly speaking.  You have to insert semicolons for the parts you wish to fill with empty statements.  In Go, the condition-only construct is a distinct type of for loop, and no placeholder semicolons are necessary.

In the third for loop form, you can omit even the condition.  This creates an infinite loop, requiring a break statement to terminate at some point.

...
consoleReader := bufio.NewReader(os.Stdin)
for {
   command, hasMoreInLine, err := bio.ReadLine()
   if err != nil || command == "exit" {
      // The user typed an "exit" command (or something went wrong).  Time to exit this loop!
      break;
   }

   // do something based on the command typed

}
...

The bare for loop is the Go equivalent to Java’s:

...
while(true) {
   String command = System.console().readLine();
   if(command.equals("exit")) {
      break;
   }

   // do something based on the command typed

}
...

switch

In Java, a switch statement executes one or more blocks of code, based on the value of some variable.  For most of Java’s history, you could only switch on primitive or enum type variables, although Java 7 finally added support for String as a switch type.

...
switch(deviceType) {
	case "PHONE" :
		renderForPhone();
		break;
	case "TABLET" :
		renderForTablet();
		break;
	case "DESKTOP" :
		renderForDesktop();
		break;
	default :
		System.out.println("Unrecognized device: " + deviceType);
}
...

In Go, a switch statement can evaluate expressions generally.  You are not limited to constants or integers, strings, etc.

...
switch customerStatus {
   case "DELINQUENT" :
      rejectOrder()
   case "GOLD_LEVEL" :
      processOrderHighPriority()
   default :
      processOrder()
}

Moreover, you do not have to tie an expression to the top-level switch statement at all!  You can utilize multiple free-form conditions with each case clause.  The conditions may be completely unrelated to each other, and can each be as complex as you like.  The first condition that passes will have its corresponding code executed:

...
switch {
   case customerStatus == "DELINQUENT" :
      rejectOrder()
   case orderAmount > 1000 || customerStatus == "GOLD_LEVEL" :
      processOrderHighPriority()
   case orderAmount > 500 :
      processOrderMediumPriority()
   default :
      processOrder()
}

Aside from flexible case conditions, the other key difference from Java’s switch is that Go’s version does NOT fall through.  In Java, a switch statement might execute multiple case blocks.  If you do not put a break statement at the end of a case block, then the next case condition will be evaluated for a match as well.

With Go, a switch statement is basically just a cleaner way of writing a messy series of if-else blocks.

Conclusion

The if, for, and switch keywords are extremely similar to their Java counterparts.  However, each of them offers extended flexibility to help you write more clear and concise logic.  In the next article of the Go for Java Programmers series, we’ll look at Go’s particular rules for well-formatted code.