BIG
DATA

JAVA

Effective Java Item - 45 : Minimize the scope of local variables

Read more about »
  • Java 9 features
  • Read about Hadoop
  • Read about Storm
  • Read about Storm
 

ITEM 45: Minimize the scope of local variables

Joshua J. Bloch says "By minimizing the scope of local variables, you increase the readability and maintainability of your code and reduce the likelihood of error".

Lets understand Item 45 from the effective java book categorized in the "General Programming" section and demystifiy what does does Joshua mean by that.

Older programming languages, such as C, mandated that local variables must be declared at the head of a block, and some programmers continue to do this out of habit. It’s a habit worth breaking. And YES, Java lets you declare variables anywhere a statement is legal.

Some programmers have a bad habit of declaring "global" variables just to avoid the inconvenience of declaring them in each and every subroutine that uses them. Call them lazy or dumb, they are wrong. I have seen Java code with a block of local variable declarations as the first thing, then the code. You could think this was written by old programmer who started his/her career in 'C'. But there are young programmers make this mistake too. You may call it 'C' hangover.

Principle-1

The most powerful and yet simple technique for minimizing the scope of a local variable is to declare it where it is first used.

It's very distracting for the reader who is trying to figure out what the program does, if a variable is declared before it is used. By the time the variable is used, the reader might not remember the variable’s type or initial value.

Declaring a local variable prematurely can cause its scope not only to extend too early, but also to end too late. The scope of a local variable extends from the point where it is declared to the end of the enclosing block. If a variable is declared outside of the block in which it is used, it remains visible after the program exits that block. If a variable is used accidentally before or after its region of intended use, the consequences can be disastrous.

Principle-2

Nearly every local variable declaration should contain an initializer.

If you don’t yet have enough information to initialize a variable sensibly, you should postpone the declaration until you do. One exception to this rule concerns trycatch statements. If a variable is initialized by a method that throws a checked exception, it must be initialized inside a try block. If the value must be used outside of the try block, then it must be declared before the try block.

Principle-3

Keep methods small and focused

One of the technique to minimize the scope of local variables is to keep methods small and focused. Your method should do one task. If you combine two task in the same method, local variables relevant to one task may be in the scope of the code performing the other task. To prevent this from happening, simply separate the method into two: one for each task.

If you do the above then you will probably be keeping the code simple and focused.As a bonus these methods are then easier and quicker to test and if a bug does sneak in then because the code is split up into smaller sections any code that needs to be fixed is isolated in it's own method.


Scope of local variables in 'for' loop

The for loop, in both its traditional and for-each forms, allows you to declare loop variables, limiting their scope to the exact region where they’re needed. This is why we should prefer for loops over while loops.

To see why these for loops are preferable to a while loop, consider the following code fragment:

Iterator i = c.iterator();
while (i.hasNext()) {
	doSomething(i.next());
}
...
Iterator i2 = c2.iterator();
while (i.hasNext()) { // BUG!
	doSomethingElse(i2.next());
}

The second loop contains a cut-and-paste error: it initializes a new loop variable, i2, but uses the old one, i, which is, unfortunately, still in scope. The resulting code compiles without error and runs without throwing an exception, but it does the wrong thing. Instead of iterating over c2, the second loop terminates immediately, giving the false impression that c2 is empty. Because the program errs silently, the error can remain undetected for a long time.

If a similar cut-and-paste error were made in conjunction with either of the for loops (for-each or traditional), the resulting code wouldn’t even compile. The element (or iterator) variable from the first loop would not be in scope at the point where the second loop occurred. Here’s how it looks for the traditional for loop:

for (Iterator i = c.iterator(); i.hasNext(); ) {
	doSomething(i.next());
}
...
// Compile-time error - cannot find symbol i
for (Iterator i2 = c2.iterator(); i.hasNext(); ) {
	doSomething(i2.next());
}

Moreover, if you use a for loop, it’s much less likely that you’ll make the cutand-paste error, as there’s no incentive to use different variable names in the two loops. The loops are completely independent, so there’s no harm in reusing the element (or iterator) variable name. In fact, it’s often stylish to do so. The for loop has one more advantage over the while loop: it is shorter, which enhances readability.

The for loop has one more advantage over the while loop: it is shorter, which enhances readability.


Noncompliant Code Example

In this noncompliant code example, You have a local variable 'i' declared in a scope that is larger than where it is used.

void testVariableScope()
{
	int i = 3;

	// i is not used here

	if (someCondition)
	{
		// i is used only within this block
	}

	// i is not used here
}


Here, the function counter() increments the global variable count and then returns immediately if this variable exceeds.Assuming that the variable count is only accessed from this function, this example is noncompliant because it does not define count within the minimum possible scope.

int count = 0;
 
void counter() {
  if (count++ > 10) 
	  return;
  /* ... */
 
}


The counter variable i is declared outside of the for loop, which goes against this recommendation because it is not declared in the block in which it is used.

int i = 0;
 
for (i=0; i < 10; i++){
  /* Perform operations */
}


Compliant Solution for above Noncompliant code

In this compliant solution, we have declared a local variable 'i' in a scope where it is used.

void testVariableScope()
{
	if (someCondition)
	{
		int i = 3;
		// i is declared inside this block
		// i is used only within this block
	}
	// some other code..
}

In this compliant solution, the variable count is declared within the scope of the counter() function

void counter() {
	int count = 0;
	if (count++ > 10) 
		return;
	/* ... */

}

In this, the variable 'i' is declared within the for loop

for (int i=0; i < 10; i++){
	  /* Perform operations */
	}


Points to remember

  • The most powerful and yet simple technique for minimizing the scope of a local variable is to declare it where it is first used
  • Nearly every local variable declaration should contain an initializer (try-catch block is an exception)
  • for loop allows to declare loop variable, and you should prefer it over while
  • Keep methods small and focused