2
public class A {
    public static void main(String[] args) {
        boolean[] test = new boolean[1];
        System.out.println(test[0]);
    }
}

prints false, as specified by the Java spec.

However,

public class A {
    public static void main(String[] args) {
        boolean test;
        System.out.println(test);
    }
}

prints

A.java:4: error: variable test might not have been initialized
        System.out.println(test);
                           ^
1 error

so, obviously: Java doesn't initialize local primitive variables - although the default value false would be valuable.

  • But why is that so, given that the default value of the type might be useful?
  • What was the reason, Java was designed in that way?
  • Furthermore, why was it not changed in later, more modern Java versions? (7,8,9)?
8
  • 3
    Initializing local variable would be a lot more expensive than initializing globals. Instead of setting values once for each class (when it is loaded), you'd have to initialize memory every time a method is called! I imagine the language designers found this too expensive, given that by not initializing you can automatically expose a lot of data flow logic bugs that would otherwise be hidden. Commented Nov 29, 2017 at 13:02
  • @KilianFoth On the flip side a reasonable optimizer doesn't need to do register allocation for a local POD variable that is initialized but unused, and a used variable which isn't initialized is typically a programmer error unless the language provides meaningful defaults. I really think the reason has more to do with preventing accidents than efficiency, since a reasonable optimizer can just avoid excessive register allocation anyway for a POD which was initialized redundantly or not even utilize a register at all if the variable is initialized but unused.
    – user204677
    Commented Nov 29, 2017 at 13:05
  • .. after all, if the compiler is smart enough to figure out that a used variable has not been initialized, then it's smart enough to omit register allocation for an initialized variable that's unused. So I think the choice to avoid default values for PODs and instead explicitly require the programmer to initialize before use was actually a deliberate design choice to prevent accidents and force developers to be more explicit.
    – user204677
    Commented Nov 29, 2017 at 13:09
  • 3
  • Are you sure it is the compiler that return that ? And not your IDE ? Eclipse sometimes show your errors that aren't compiler's one because they do are relevant even if it doesn't prevent the compilation.
    – Walfrat
    Commented Nov 29, 2017 at 14:31

3 Answers 3

4

In Short a language design issue as successor of C++

Fields (as opposed to variables) can only be initialized in object's constructor or in an initializer. In C++ this should have been done at the time with a constructor like Point::Point(): x(0), y(0) {}. They wanted to remove that boiler plate code, and the entire : .... So zeroing of the object. Mind that for final fields still assigment checking is done.

Local (primitive and pointer) variables in C++ are not initialized at that time, and the compiler can check whether every flow initializes the variable. This is less error prone, than a default initialisation. For instance if a following if-else is expected to initialize the variable with two different values but gets forgotten in one branch. A default, especially null, is rarely a sensible value for a local variable.

The code cost of initializing a local variable with a default value first, is neglectable, and a compiler might even consider not initializing with a default, when the control flow initializes itself.

2

This is only conjecture but it's possibly the safest design for PODs if the language doesn't force initialization at the point of definition for the compiler to be able to detect uninitialized variables and post errors.

A compile-time error is preferable to a surprise at runtime, and initializing these to default values like false for a boolean could end up being something the programmer did not actually want -- although initializing variables with default values is still preferable to garbage as in C since it at least provides deterministic behavior.

Anyway, that would be my best guess -- it's done to prevent accidents from occurring where a programmer might have intended to set a boolean to true, e.g., but forgot to initialize it. The language designers decided to make that an error instead of making it default to false. The designers decided to explicitly require programmers to spell things out instead of having implicit default behavior which might not match what they intended.

Furthermoore, why was it not changed in later, more modern java versions ? (7,8,9) ?

If it started out this way and the rationale above matches the designers of the language, then it's not a safe change to turn what was formerly an error into passable runtime behavior. Suddenly what would have been former errors in old code now pass without error in ways that could go from code that doesn't build where the compiler can point out what it's doing wrong to code which can suddenly run but only in ways that cause runtime behavior which was different from what the programmer originally intended. From a language design standpoint, if it started out this way, then you want to keep it this way indefinitely.

But isn't it the other way round? If a program was never legal (i.e. resulted in compiler error, or invoked unspecified behaviour), then it is safe for the language designers to assign some meaning to this program.

I wanted to add this question into the comment since it's such a good one that really made me think a lot about it. I would suggest a strong "no" but need some careful explanation. The gist of it is that you cannot confidently introduce implicit functionality into a language or a library that formerly considered the original behavior where the programmer failed to specify his intentions a mistake, because we cannot assume the implicit behavior we're inserting in place of the former mistake was in line with his original unspecified intentions. A language that was originally built with the specification that boolean x will have a default value of false is fine, but lacking such a specification so late in the game, we can't suddenly decide we can read people's minds.

These build errors are not the result of limitations, they are the result of the language designers intentionally considering the failure for the programmer to specify intent a real human error. It's not the type of case where you can go back on such a firm design decision in the language with a historical design rationale behind it in ways that can guarantee no horrific side effects in all possible cases. To provide default implicit behavior is not expanding the language capabilities in this case, it's reverting a fundamental design decision at the heart of the type system, and reverting fundamental design decisions in a mature language or library is generally something to strongly err against doing.

I might come off like I'm being paranoid and too strict/dogmatic, but such is often the requirement to maintain a mature language or library with countless lines of code produced by people across the world for years written against it. We can't suddenly decide to go back on fundamental design decisions in ways that alters the behavior of all code ever written for said language/library far removed from our control, even if it's just turning a formerly decided error into a non-error. That's not an expansion of the language, that's a fundamental design change and a no-no in the majority of cases unless absolutely required, and providing implicit default values for PODs was practically never a requirement for Java devs to get on with their day.

For example, if former code had boolean x and tried to use it, you can't suddenly go from the language treating that like a mistake to then being able to assume that the programmer intended x to be false, since the former design he wrote the code against never specified that behavior. He might have wanted x to be true, and now instead of code which just fails to run, the code might now run and cause sporadic bugs that are hard to ever trace down to this cause (far, far worse than code that simply fails to run).

As an analogical example which will more clearly highlight the problem, right now Java does not allow arrays to be indexed with negative numbers (some languages provide this and allow circular indexing). To do so is considered a programmer mistake and error, and the system will throw an out of bounds exception. However, if the language designers suddenly change their mind and decide negative indexing of plain old arrays should now be allowed and that it should be fine to introduce because former code that negatively indexed arrays were never working in the first place, well now you might have turned horrible programmer mistakes which could have been caught on execution into non-mistakes with an assumption that this is now okay. The practical end result could be that we've now obscured and hidden some very, very deadly bugs introduced in code written against prior Java versions before it supported negative indexing.

8
  • 2
    But isn't it the other way round? If a program was never legal (i.e. resulted in compiler error, or invoked unspecified behaviour), then it is safe for the language designers to assign some meaning to this program. After all, this doesn't change the behaviour of any working program, only of programs that couldn't work previously.
    – amon
    Commented Nov 29, 2017 at 13:32
  • Quite the opposite to me because the error is actually desirable if the language intended it originally as such. As an analogical example, imagine you had a library you designed where people weren't allowed to pass in negative values to a function. That would result in an error. It wouldn't be so desirable generally to later turn that into a non-error by having the same function now accept negative values and turn what was formerly an error into a non-error. Instead it's generally opening far less cans of worms to just require people to fix their original code against the error.
    – user204677
    Commented Nov 29, 2017 at 14:18
  • ... this is assuming there was originally a solid rationale for why certain things were considered errors in the first place and not as a result of some limitation. If such is the case, I think there's more compelling reasons to keep them as errors instead of turn them into non-errors.
    – user204677
    Commented Nov 29, 2017 at 14:19
  • @amon Hmm, I feel like I'm explaining it poorly. But if I put myself in the shoes of the Java language designers, and I decided that we're going to make it an error for people to try to use an uninitialized boolean and we aren't going to do things like implicitly give them a default value of false to force programmers to explicitly spell out their intentions without hidden implicit behaviors, then I think it makes sense to keep that original design rationale indefinitely so long as the language exists. Personally I like default values, but if I thought that way, I wouldn't want to [...]
    – user204677
    Commented Nov 29, 2017 at 14:24
  • [...] revert that design decision in the future and suddenly allowed code that formerly didn't compile to compile with new implicit behaviors provided in places the programmers failed to specify their intentions. That's asking for trouble to throw in new implicit behavior into the language where explicitness was formerly required to avoid error. As another example, imagine in the future that in C++, we decided to make it so a function that doesn't specify its return type will suddenly be implicitly assumed to be a void function. That's asking for trouble because we're turning [...]
    – user204677
    Commented Nov 29, 2017 at 14:25
1

Failing to initialise a value is a logic error on the part of the programmer, and should be caught as soon as possible. Java does this at compile time for locals, but only at runtime (or not at all) for fields.

The question "Why is it not an error to provide no value to a field?" is then pertinent. In the case of a local variable, you can easily verify that all possible code paths contain initialisation before use, and being conservative in this analysis isn't overly onerous on the programmer. Rejecting constructions that are only just beyond the analysis, e.g.

{
    bool toggle = false;
    String relevant;
    if (toggle) {
        relevant = "True path";
    } else if (!toggle) {
        relevant = "False path";
    }
    // No else! but we don't need one...
    System.out.printLn(relevant);
}

is a small price to pay at the level of a single method, but at the level of a class would put people off using the language, e.g.

class Example {
    Example() {}
    Example(String r) { relevant = r; }
    String relevant;
    private void TruePath() {
        relevant = "True path";
    }
    private void FalsePath() {
        relevant = "False path";
    }
    public void DoStuff(bool toggle) {
        if (toggle) {
            TruePath();
        } else if (!toggle) {
            FalsePath();
        }
        // Is relevant always initialised?
        // How should the compiler know?
        System.out.printLn(relevant);
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.