views:

319

answers:

6

For this Java code:

String var;
clazz.doSomething(var);

Why does the compiler report this error:

Variable 'var' might not have been initialized

I thought all variables or references were initialized to null. Why do you need to do:

String var = null;

??

+16  A: 

Instance and class variables are initialized to null (or 0), but local variables are not.

See §4.12.5 of the JLS for a very detailed explanation which says basically the same thing:

Every variable in a program must have a value before its value is used:

  • Each class variable, instance variable, or array component is initialized with a default value when it is created:
    • [snipped out list of all default values]
  • Each method parameter is initialized to the corresponding argument value provided by the invoker of the method.
  • Each constructor parameter is initialized to the corresponding argument value provided by a class instance creation expression or explicit constructor invocation.
  • An exception-handler parameter is initialized to the thrown object representing the exception.
  • A local variable must be explicitly given a value before it is used, by either initialization or assignment, in a way that can be verified by the compiler using the rules for definite assignment.
Michael Myers
Why was that decision made? Why not just init them to `null` like class variables?
Marcus
Perhaps for better code/readability?
matt b
There are many stackoverflow questions regarding why local variables don't have defaults. e.g. http://stackoverflow.com/questions/415687/why-are-local-variables-not-initialized-in-java
Michael Krauklis
@Marcus @matt b: Yes, it's likely because not initializing a local variable is almost always the sign of a bug.
Michael Myers
+6  A: 

It's because Java is being very helpful (as much as possible).

It will use this same logic to catch some very interesting edge-cases that you might have missed. For instance:

int x;

if(cond2)
   x=2;
else if(cond3)
   x=3;

System.out.println("X was:"+x);

This will fail because there was an else case that wasn't specified. The fact is, an else case here should absolutely be specified, even if it's just an error (The same is true of a default: condition in a switch statement).

What you should take away from this, interestingly enough, is don't ever initialize your local variables until you figure out that you actually have to do so. If you are in the habit of always saying "int x=0;" you will prevent this fantastic "bad logic" detector from functioning. This error has saved me time more than once.

Bill K
+1  A: 

Ditto on Bill K. I add:

The Java compiler can protect you from hurting yourself by failing to set a variable before using it within a function. Thus it explicitly does NOT set a default value, as Bill K describes.

But when it comes to class variables, it would be very difficult for the compiler to do this for you. A class variable could be set by any function in the class. It would be very difficult for the compiler to determine all possible orders in which functions might be called. At the very least it would have to analyze all the classes in the system that call any function in this class. It might well have to examine the contents of any data files or database and somehow predict what inputs users will make. At best the task would be extremely complex, at worst impossible. So for class variables, it makes sense to provide a reliable default. That default is, basically, to fill the field with bits of zero, so you get null for references, zero for integers, false for booleans, etc.

As Bill says, you should definitely NOT get in the habit of automatically initializing variables when you declare them. Only initialize variables at declaration time if this really make sense in the context of your program. Like, if 99% of the time you want x to be 42, but inside some IF condition you might discover that this is a special case and x should be 666, then fine, start out with "int x=42;" and inside the IF override this. But in the more normal case, where you figure out the value based on whatever conditions, don't initialize to an arbitrary number. Just fill it with the calculated value. Then if you make a logic error and fail to set a value under some combination of conditions, the compiler can tell you that you screwed up rather than the user.

PS I've seen a lot of lame programs that say things like:

HashMap myMap=new HashMap();
myMap=getBunchOfData();

Why create an object to initialize the variable when you know you are promptly going to throw this object away a millisecond later? That's just a waste of time.

Edit

To take a trivial example, suppose you wrote this:

int foo;
if (bar<0)
  foo=1;
else if (bar>0)
  foo=2;
processSomething(foo);

This will throw an error at compile time, because the compiler will notice that when bar==0, you never set foo, but then you try to use it.

But if you initialize foo to a dummy value, like

int foo=0;
if (bar<0)
  foo=1;
else if (bar>0)
  foo=2;
processSomething(foo);

Then the compiler will see that no matter what the value of bar, foo gets set to something, so it will not produce an error. If what you really want is for foo to be 0 when bar is 0, then this is fine. But if what really happened is that you meant one of the tests to be <= or >= or you meant to include a final else for when bar==0, then you've tricked the compiler into failing to detect your error. And by the way, that's way I think such a construct is poor coding style: Not only can the compiler not be sure what you intended, but neither can a future maintenance programmer.

Jay
Why not just initialize it to null? The only real problematic case I can think of is when you should declare a final variable that has to be set a value in a try block. But you can just define a temporary variable here and assign its value after the try block to a final one.
sibidiba
@sibidiba: What part of my answer are you replying to? If you mean in general, then you would lose the advantage of the compile-time checking. See Bill K's answer. If you mean my closing comment with the myMap example, then the easy solution is to write "HashMap myMap=getBunchOfData()" and skip the pointless extra object.
Jay
+1  A: 

I like Bill K's point about letting the compiler work for you- I had fallen into initializing every automatic variable because it 'seemed like the Java thing to do'. I'd failed to understand that class variables (ie persistent things that constructors worry about) and automatic variables (some counter, etc) are different, even though EVERYTHING is a class in Java.

So I went back and removed the initialization I'd be using, for example

List <Thing> somethings = new List<Thing>(); 
somethings.add(somethingElse); // <--- this is completely unnecessary

Nice. I'd been getting a compiler warning for

List<Thing> somethings = new List(); 

and I'd thought the problem was lack of initialization. WRONG. The problem was I hadn't understood the rules and I needed the <Thing> identified in the "new", not any actual items of type <Thing> created.

(Next I need to learn how to put literal less-than and greater-than signs into HTML!)

Bill IV
@Bill IV I cleaned up the quotes for you; all you need to do is designate the code sections as code (the 101/010 icon above the editor will do it).
Carl Manaster
Thank you, Carl!
Bill IV
A: 

I don't know the logic behind it, but local variables are not initialized to null. I guess to make your life easy. They could have done it with class variables if it were possible. It doesn't mean you have to have it initialized in the beginning. This is fine :

MyClass cls;
if (condition) {
    cls = something;
else 
    cls = something_else;
fastcodejava
A: 

Hi Jay, Sure, if you've really got two lines on top of each other as you show- declare it, fill it, no need for a default constructor. But, for example, if you want to declare something once and use it several or many times, the default constructor or null declaration is relevant. Or is the pointer to an object so lightweight that its better to allocate it over and over inside a loop, because the allocation of the pointer is so much less than the instantiation of the object? (Presumably there's a valid reason for a new object at each step of the loop).

Bill IV

Bill IV
I'm not saying, Don't create a value and re-use it. I'm saying, Avoid filling in a dummy value as a placeholder until you find the real value. If you just declare the variable but leave it unfilled, then later you go through all your IFs and BUTs to fill it, then you try to use it, the compiler will tell you if there is some possible path through your where you never filled it. But if you fill in a dummy value, the compiler has no way to know that that's a dummy value and not a real value, and so you lose the advantage of the compile-time checking.
Jay