"Local variable is redundant" using Java - java

Why is the following giving me a "local variable is redundant error"?
public double depreciationAmount() {
double depreciationAmount = (cost * percentDepreciated);
return depreciationAmount;
}

Why is the following giving me a "local variable is redundant error"?
Because you can trivially write this without using a local variable.
public double depreciationAmount() {
return cost * percentDepreciated;
}
Hence the local variable is deemed to be unnecessary / redundant by the checker.
However, I surmise that this is not a compiler error. It might be a compiler warning, or more likely it is a style checker or bug checker warning. It is something you could ignore without any risk to the correctness of your code ... as written.
Also, I would predict that once that the code has been JIT compiled (by a modern Hotspot JIT compiler ...) there would be no performance difference between the two versions.
I won't attempt to address the issue as to whether the warning is appropriate1. If you feel it is inappropriate, then "Local variable is redundant" using Java explains how to suppress it.
1 - Except to say that it is too much to expect current generation style checkers to know when so-called explaining variables are needed. First you'd need to get a statistically significant2 group of developers to agree on measurable3 criteria for when the variables are needed, and when they aren't.
2 - Yea, I know. Abuse of terminology.
3 - They must be measurable, and there needs to be consensus on what the thresholds should be if this is to be implemented by a checker.

Although not the case here, if having a redundant local variable is desired (I've had one time where this was the case - without getting into specifics), here's how to suppress this specific warning.
#SuppressWarnings("UnnecessaryLocalVariable")
public double depreciationAmount() {
double depreciationAmount = (cost * percentDepreciated);
return depreciationAmount;
}

You only use the value of percentDepreciated to return it when you could have just done return (cost * percentDepreciated).

Why is the following giving me a "local variable is redundant error"?
I believe this message is wrong. Your depreciationAmount variable assignment is totally fine. Moreover, I always prefer this kind of assignment before return, because it helps to avoid confusion while debugging.
In this example, the getValue() method returns an expression result, instead of assigning the expression result to a variable.
Now when I use debugger watch, to know the result of an expression, I got a confusion. My program ends with the wrong result and debugger watch values are inconsistent. It would be easy to avoid this, if I would have a variable assigned before the returning expression:
Integer value = 1 + getCounter();
return value;
instead of:
return 1 + getCounter();
Now I can put a breakpoint at the return statement and know what was the result of the expression, before it was returned. Also I do not need the expression in the watch any more, and code will be executed correctly while debugging.

In computer programming, redundant code is source code or compiled code in a computer program that is unnecessary. In the above code, you can simply return:
(cost * percentDepreciated)

Related

Why, when checking compile-time constant, one branch is enough for resolving variable assignation, but is not enough for returning from that branch?

We know, that if if statement's boolean expression/condition contains compile-time constant (or a variable, holding compile-time constant), then compiler can resolve this constant expression and:
public void ctc1() {
final int x = 1;
String text;
if (x > 0) text = "some text";
System.out.println(text); //compiles fine, as compile-time constant is resolved during compilation phase;
}
would compile and work fine, without "variable might not have been initialized" compiler error. No initialisation of text in "else" branch (or after "if") is required, as compiler "understands" the constant is always going to result in true, while evaluating x > 0 (which ends up being 1 > 0).
Why, however, same resolution does not work (or works differently) when we want to return from the method, as:
public int ctc2() {
final int x = 1;
if (x > 0) return 1;
//requires another explicit "return" for any other condition, than x > 0
}
or, moreover, as:
public int ctc2() {
final int x = 1;
if (1 > 0) return 1;
}
?
Why compiler cannot infer/understand/resolve absolutely identical semantics and cannot be sure, that the return is always executed and code is OK to be compiled?
In case of initialisation in the branch containing compile-time constant, compiler can resolve the constant value(s), and as it knows they will never change, it is sure, variable is going to be initialised. So, it allows usage of the variable after if statement.
Why resolving constant expression works differently for the return case, though? what is the point behind this difference/limitation?
To me, this looks like "two identical logical semantics" work differently.
I'm using:
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
This is a difference (mismatch? inconsistency?) between the rules around definite assignment and the particular normal completion rules of the if statement.
Specifically, the definite assignment rules say:
In all, there are four possibilities for a variable V after a statement or expression has been executed:
V is definitely assigned and is not definitely unassigned.
(The flow analysis rules prove that an assignment to V has occurred.)
...
The "flow analysis rules" are not clearly specified with regard to branch pruning, but it doesn't seem unreasonable to assume that the flow analysis is able to take into account constant values when deciding whether to follow a branch, meaning it is able to determine there is only one of the 4 states possible (definitely assigned after the if statement).
However, the reachability rules for an if statement say that:
An if-then statement can complete normally iff it is reachable.
Nothing about the expression value or flow analysis here. It's perhaps worth pointing out that this is itself different to the reachability rules for while, do and basic for loops, which do explicitly mention the case of a constant true expression. Any of these returns would be accepted in ctc2():
while (true) return 1;
do { return 1; } while (true);
for (;;) return 1;
So, the language is specified in such a way that it overlooks the fact that your if statement cannot complete normally because of a) the constant expression, b) the return statement, despite that being "obvious" to a human reader.
An example of this difference actually being desirable (or, at least, the reachability rules being desirable) is if you have a DEBUG boolean (as in, a constant-valued to trigger debug-only behaviour). You can imagine a method something like:
if (!DEBUG) {
return value;
}
return otherValue;
If the "conditional" return were treated in the same way as definite assignment, at least one of the return statements unreachable.
This would be a pain for debugging-time alternate behaviour like this.
Ofc one might argue that you could instead do something that isn't compile-time constant, e.g. invoke a method. I guess you can do that, but I would argue that not allowing use of the dirt-simplest method is.... unnecessarily restrictive, for the sake of avoiding a pretty rare "head-scratcher" in code.

Does Java automatically save an 'evaluate' statement in a predefined Java variable?

Say I perform a simple add/concatenation statement:
variable + newInput
Without setting the calculated value to a new variable, as in:
variable = variable + newInput
or
variable += newInput
Does Java have some sort of specifier to be able to use the computed sum or concatenated string?
Apparently in Python it is automatically saved in the implicit global variable _ -which is implementable like
Print(_)
Is there anything like this in Java?
No. It does not have anything like this. You have to assign the computed value to a variable, otherwise it will be lost and consequently collected by the garbage collector.
The best option is to use a special operator so not to use an extra variable but assign the result to an old one. This is a Shorthand operator.
Variable += NewInput
More than just not saving the result, Java will outright refuse to compile your program if it contains such a line, precisely because the result would be unsaved and unusable if it was allowed:
public class Main
{
public static void main(String[] args)
{
1+2;
}
}
Result:
Main.java:5: error: not a statement
1+2;
^
1 error
Java does not allow arbitrary expressions as statements, and addition expressions are not considered valid Java statements.
The expressions that are allowed as statements by themselves are listed in the JLS:
ExpressionStatement:
StatementExpression ;
StatementExpression:
Assignment
PreIncrementExpression
PreDecrementExpression
PostIncrementExpression
PostDecrementExpression
MethodInvocation
ClassInstanceCreationExpression
Assignment, increment, decrement, method calls, and new Whatever(), all things with side effects or potential side effects. Barring possible side effects of an implicit toString() call, + cannot have side effects, so to catch probable errors, Java forbids addition expressions from being statements.
You can for sure do:
variable + newInput
but the result of that operation must be assigned to a variable, if not, it will get lost...
the most you can get is
variable += newInput
whihch is similar to
variable = variable + newInput
The point is: the + operator in Java simply takes two operands and returns a result (either numerical, or as string concatenation).
Without assigning this result to something (like returning it from a method; or as shown in your example) ... it is like: the operation never takes place.
This operation doesn't have any side effects on its operands; and there is no way of accessing this result.
Beyond that, there is no operator overloading in Java. So it is also not possible to do some black magic that somehow stores the result of operation as side effect. You could theoretically add an agent to the JVM, that intercepts at runtime to do something upon an add operation, but that is more like: "technically possible", but nothing you would do in practical reality.
Other JVM languages, like Scala for example might use it implicitly - the last expression in a method is always returned, even when leaving out the return statement (in scala).
The statement you show is evaluated and nothing is done with it. Unless you bind a variable to the result, the evaluation occurs without effect.
This will not be flagged as an error by the compiler.

In java method why we need to initialize a variable if we want to return it [duplicate]

Was there any reason why the designers of Java felt that local variables should not be given a default value? Seriously, if instance variables can be given a default value, then why can't we do the same for local variables?
And it also leads to problems as explained in this comment to a blog post:
Well this rule is most frustrating when trying to close a resource in a finally block. If I instantiate the resource inside a try, but try to close it within the finally, I get this error. If I move the instantiation outside the try, I get another error stating that a it must be within a try.
Very frustrating.
Local variables are declared mostly to do some calculation. So it's the programmer's decision to set the value of the variable and it should not take a default value.
If the programmer, by mistake, did not initialize a local variable and it takes a default value, then the output could be some unexpected value. So in case of local variables, the compiler will ask the programmer to initialize it with some value before they access the variable to avoid the usage of undefined values.
The "problem" you link to seems to be describing this situation:
SomeObject so;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
so.CleanUp(); // Compiler error here
}
The commenter's complaint is that the compiler balks at the line in the finally section, claiming that so might be uninitialized. The comment then mentions another way of writing the code, probably something like this:
// Do some work here ...
SomeObject so = new SomeObject();
try {
so.DoUsefulThings();
} finally {
so.CleanUp();
}
The commenter is unhappy with that solution because the compiler then says that the code "must be within a try." I guess that means some of the code may raise an exception that isn't handled anymore. I'm not sure. Neither version of my code handles any exceptions, so anything exception-related in the first version should work the same in the second.
Anyway, this second version of code is the correct way to write it. In the first version, the compiler's error message was correct. The so variable might be uninitialized. In particular, if the SomeObject constructor fails, so will not be initialized, and so it will be an error to attempt to call so.CleanUp. Always enter the try section after you have acquired the resource that the finally section finalizes.
The try-finally block after the so initialization is there only to protect the SomeObject instance, to make sure it gets cleaned up no matter what else happens. If there are other things that need to run, but they aren't related to whether the SomeObject instance was property allocated, then they should go in another try-finally block, probably one that wraps the one I've shown.
Requiring variables to be assigned manually before use does not lead to real problems. It only leads to minor hassles, but your code will be better for it. You'll have variables with more limited scope, and try-finally blocks that don't try to protect too much.
If local variables had default values, then so in the first example would have been null. That wouldn't really have solved anything. Instead of getting a compile-time error in the finally block, you'd have a NullPointerException lurking there that might hide whatever other exception could occur in the "Do some work here" section of the code. (Or do exceptions in finally sections automatically chain to the previous exception? I don't remember. Even so, you'd have an extra exception in the way of the real one.)
Moreover, in the example below, an exception may have been thrown inside the SomeObject construction, in which case the 'so' variable would be null and the call to CleanUp will throw a NullPointerException
SomeObject so;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
so.CleanUp(); // Compiler error here
}
What I tend to do is this:
SomeObject so = null;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
if (so != null) {
so.CleanUp(); // safe
}
}
The actual answer to your question is because method variables are instantiated by simply adding a number to the stack pointer. To zero them would be an extra step. For class variables they are put into initialized memory on the heap.
Why not take the extra step? Take a step back--Nobody mentioned that the "warning" in this case is a Very Good Thing.
You should never initialize your variable to zero or null on the first pass (when you are first coding it). Either assign it to the actual value or don't assign it at all because if you don't then Java can tell you when you really screw up. Take Electric Monk's answer as a great example. In the first case, it's actually amazingly useful that it's telling you that if the try() fails because SomeObject's constructor threw an exception, then you would end up with an NPE in the finally. If the constructor can't throw an exception, it shouldn't be in the try.
This warning is an awesome multi-path bad programmer checker that has saved me from doing stupid stuff since it checks every path and makes sure that if you used the variable in some path then you had to initialize it in every path that lead up to it. I now never explicitly initialize variables until I determine that it is the correct thing to do.
On top of that, isn't it better to explicitly say "int size=0" rather than "int size" and make the next programmer go figure out that you intend it to be zero?
On the flip side I can't come up with a single valid reason to have the compiler initialize all uninitialized variables to 0.
Notice that the final instance/member variables don't get initialized by default. Because those are final and can't be changed in the program afterwards. That's the reason that Java doesn't give any default value for them and force the programmer to initialize it.
On the other hand, non-final member variables can be changed later. Hence, the compiler doesn't let them remain uninitialised; precisely, because those can be changed later. Regarding local variables, the scope of local variables is much narrower; and compiler knows when it's getting used. Hence, forcing the programmer to initialize the variable, makes sense.
For me, the reason comes down to this this: The purpose of local variables is different than the purpose of instance variables. Local variables are there to be used as part of a calculation; instance variables are there to contain state. If you use a local variable without assigning it a value, that's almost certainly a logic error.
That said, I could totally get behind requiring that instance variables were always explicitly initialized; the error would occur on any constructor where the result allows an uninitialized instance variable (e.g., not initialized at declaration and not in the constructor). But that's not the decision Gosling, et. al., took in the early 90's, so here we are. (And I'm not saying they made the wrong call.)
I could not get behind defaulting local variables, though. Yes, we shouldn't rely on compilers to double-check our logic, and one doesn't, but it's still handy when the compiler catches one out. :-)
I think the primary purpose was to maintain similarity with C/C++. However the compiler detects and warns you about using uninitialized variables which will reduce the problem to a minimal point. From a performance perspective, it's a little faster to let you declare uninitialized variables since the compiler will not have to write an assignment statement, even if you overwrite the value of the variable in the next statement.
It is more efficient not to initialize variables, and in the case of local variables it is safe to do so, because initialization can be tracked by the compiler.
In cases where you need a variable to be initialized you can always do it yourself, so it is not a problem.
The idea behind local variables is they only exist inside the limited scope for which they are needed. As such, there should be little reason for uncertainty as to the value, or at least, where that value is coming from. I could imagine many errors arising from having a default value for local variables.
For example, consider the following simple code... (N.B. let us assume for demonstration purposes that local variables are assigned a default value, as specified, if not explicitly initialized)
System.out.println("Enter grade");
int grade = new Scanner(System.in).nextInt(); // I won't bother with exception handling here, to cut down on lines.
char letterGrade; // Let us assume the default value for a char is '\0'
if (grade >= 90)
letterGrade = 'A';
else if (grade >= 80)
letterGrade = 'B';
else if (grade >= 70)
letterGrade = 'C';
else if (grade >= 60)
letterGrade = 'D';
else
letterGrade = 'F';
System.out.println("Your grade is " + letterGrade);
When all is said and done, assuming the compiler assigned a default value of '\0' to letterGrade, this code as written would work properly. However, what if we forgot the else statement?
A test run of our code might result in the following
Enter grade
43
Your grade is
This outcome, while to be expected, surely was not the coder's intent. Indeed, probably in a vast majority of cases (or at least, a significant number, thereof), the default value wouldn't be the desired value, so in the vast majority of cases the default value would result in error. It makes more sense to force the coder to assign an initial value to a local variable before using it, since the debugging grief caused by forgetting the = 1 in for(int i = 1; i < 10; i++) far outweighs the convenience in not having to include the = 0 in for(int i; i < 10; i++).
It is true that try-catch-finally blocks could get a little messy (but it isn't actually a catch-22 as the quote seems to suggest), when for example an object throws a checked exception in its constructor, yet for one reason or another, something must be done to this object at the end of the block in finally. A perfect example of this is when dealing with resources, which must be closed.
One way to handle this in the past might be like so...
Scanner s = null; // Declared and initialized to null outside the block. This gives us the needed scope, and an initial value.
try {
s = new Scanner(new FileInputStream(new File("filename.txt")));
int someInt = s.nextInt();
} catch (InputMismatchException e) {
System.out.println("Some error message");
} catch (IOException e) {
System.out.println("different error message");
} finally {
if (s != null) // In case exception during initialization prevents assignment of new non-null value to s.
s.close();
}
However, as of Java 7, this finally block is no longer necessary using try-with-resources, like so.
try (Scanner s = new Scanner(new FileInputStream(new File("filename.txt")))) {
...
...
} catch(IOException e) {
System.out.println("different error message");
}
That said, (as the name suggests) this only works with resources.
And while the former example is a bit yucky, this perhaps speaks more to the way try-catch-finally or these classes are implemented than it speaks about local variables and how they are implemented.
It is true that fields are initialized to a default value, but this is a bit different. When you say, for example, int[] arr = new int[10];, as soon as you've initialized this array, the object exists in memory at a given location. Let's assume for a moment that there is no default values, but instead the initial value is whatever series of 1s and 0s happens to be in that memory location at the moment. This could lead to non-deterministic behavior in a number of cases.
Suppose we have...
int[] arr = new int[10];
if(arr[0] == 0)
System.out.println("Same.");
else
System.out.println("Not same.");
It would be perfectly possible that Same. might be displayed in one run and Not same. might be displayed in another. The problem could become even more grievous once you start talking reference variables.
String[] s = new String[5];
According to definition, each element of s should point to a String (or is null). However, if the initial value is whatever series of 0s and 1s happens to occur at this memory location, not only is there no guarantee you'll get the same results each time, but there's also no guarantee that the object s[0] points to (assuming it points to anything meaningful) even is a String (perhaps it's a Rabbit, :p)! This lack of concern for type would fly in the face of pretty much everything that makes Java Java. So while having default values for local variables could be seen as optional at best, having default values for instance variables is closer to a necessity.
Flip this around and ask: why are fields initialised to default values? If the Java compiler required you to initialise fields yourself instead of using their default values, that would be more efficient because there would be no need to zero out memory before you used it. So it would be a sensible language design if all variables were treated like local variables in this regard.
The reason is not because it's more difficult to check this for fields than for local variables. The Java compiler already knows how to check whether a field is definitely initialised by a constructor, because it has to check this for final fields. So it would be little extra work for the compiler to apply the same logic to other fields to ensure they are definitely assigned in the constructor.
The reason is that, even for final fields where the compiler proves that the field is definitely assigned in the constructor, its value before assignment can still be visible from other code:
class A {
final int x;
A() {
this.x = calculate();
}
int calculate() {
System.out.println(this.x);
return 1;
}
}
In this code, the constructor definitely assigns to this.x, but even so, the field's default initial value of 0 is visible in the calculate method at the point where this.x is printed. If the field wasn't zeroed out before the constructor was invoked, then the calculate method would be able to observe the contents of uninitialised memory, which would be non-deterministic behaviour and have potential security concerns.
The alternative would be to forbid the method call calculate() at this point in the code where the field isn't yet definitely assigned. But that would be inconvenient; it is useful to be able to call methods from the constructor like this. The convenience of being able to do that is worth more than the tiny performance cost of zeroing out the memory for the fields before invoking the constructor.
Note that this reasoning does not apply to local variables, because a method's uninitialised local variables are not visible from other methods; because they are local.
Eclipse even gives you warnings of uninitialized variables, so it becomes quite obvious anyway. Personally I think it's a good thing that this is the default behaviour, otherwise your application may use unexpected values, and instead of the compiler throwing an error it won't do anything (but perhaps give a warning) and then you'll be scratching your head as to why certain things don't quite behave the way they should.
Instance variable will have default values but the local variables could not have default values. Since local variables basically are in methods/behavior, its main aim is to do some operations or calculations. Therefore, it is not a good idea to set default values for local variables. Otherwise, it is very hard and time-consuming to check the reasons of unexpected answers.
The local variables are stored on a stack, but instance variables are stored on the heap, so there are some chances that a previous value on the stack will be read instead of a default value as happens in the heap.
For that reason the JVM doesn't allow to use a local variable without initializing it.
Memory stack for methods is created at execution time. The method stack order is decided at execution time.
There might be a function that may not be called at all. So to instantiate local variables at the time of object instantiation would be a complete wastage of memory. Also, Object variables remain in memory for a complete object lifecycle of a class whereas, local variables and their values become eligible for garbage collection the moment they are popped from the memory stack.
So, To give memory to the variables of methods that might not even be called or even if called, will not remain inside memory for the lifecycle of an object, would be a completely illogical and memory-waste-worthy
The answer is instance variables can be initialized in the class constructor or any class method. But in case of local variables, once you defined whatever in the method, that remains forever in the class.
I could think of the following two reasons
As most of the answers said, by putting the constraint of initialising the local variable, it is ensured that the local variable gets assigned a value as the programmer wants and ensures the expected results are computed.
Instance variables can be hidden by declaring local variables (same name) - to ensure the expected behaviour, local variables are forced to be initialised to a value (I would totally avoid this, though).

Java "new String[-1]" passes compilation. How come?

While fiddling around in Java, I initialized a new String array with a negative length.
i.e. -
String[] arr = new String[-1];
To my surprise, the compiler didn't complain about it.
Googling didn't bring up any relevant answers. Can anyone shed some light on this matter?
Many thanks!
The reason is that the JLS allows this, and a compiler that flagged it as a compilation error would be rejecting valid Java code.
It is specified in JLS 15.10.1. Here's the relevant snippet:
"... If the value of any DimExpr expression is less than zero, then a NegativeArraySizeException is thrown."
Now if the Java compiler flagged the code as an error, then that specified behaviour could not occur ... in that specific code.
Furthermore, there's no text that I can find that "authorizes" the compiler to reject this in the "obvious mistake" cases involving compile-time constant expressions like -1. (And who is to say it really was a mistake?)
The next question, of course, is 'why does the JLS allow this?'
You've need to ask the Java designers. However I can think of some (mostly) plausible reasons:
This was originally overlooked, and there's no strong case for fixing it. (Noting that fixing it breaks source code compatibility.)
It was considered to be too unusual / edge case to be worth dealing with.
It would potentially cause problems for people writing source code generators. (Imagine, having to write code to evaluate compile-time constant expressions in order that you don't generate non-compilable code. With the current JLS spec, you can simply generate the code with the "bad" size, and deal with the exception (or not) if the code ever gets executed.)
Maybe someone had a plan to add "unarrays" to Java :-)
Other answers have suggested that the compiler could / should "flag" this case. If "flagging" means outputting a warning message, that is certainly permitted by the JLS. However, it is debatable whether the compiler should do this. On the one hand, if the above code was written by mistake, then it would be useful to have that mistake flagged. On the other hand, if it was not a mistake (or the "mistake" was not relevant) then the warning would be noise, or worse. Either way, this is something that you would need to discuss with the maintainer(s) for the respective compiler(s).
I see no reason why this couldn't be flagged up at compile time (at least as a warning), since this unconditionally throws NegativeArraySizeException when executed.
I've done some quick experiments with my compiler, and it seems surprisingly relaxed about this sort of thing. It issues no warning about integer divide by zero in constant expressions, out-of-bounds array access with constant indices etc.
From this I conclude that the general pattern here is to trust the programmer.
Compiler only responsible for checking language syntax, but not the semantic meaning of you code.
Thus it is reasonable the compiler is not complaining error as there is no syntax error in your code at all.
In Java, array is allocated at runtime, which is absolutely ok. If it is allocate at compile time, then how compiler check the following code?
// runtime pass the length, with any value
void t(int length) {
String[] stirngs = new String[length];
}
When pass negative value as length to contruct array, the runtime exception will being thrown.
public class Main {
public static void main(String[] args) {
String[] v = new String[-1];
}
}
with error:
Exception in thread "main" java.lang.NegativeArraySizeException
at Main.main(Main.java:5)
Java compiler takes an integer as the length of an array. It can be a variable or a compile-time constant. The length of an array is established when the array is created. After creation, its length is fixed.
The compiler should flag a negative compile-time constant as the length of an array. It just does not do so . If the length is a negative number you will get a NegativeArraySizeException at run time.

In Java, should variables be declared at the top of a function, or as they're needed?

I'm cleaning up Java code for someone who starts their functions by declaring all variables up top, and initializing them to null/0/whatever, as opposed to declaring them as they're needed later on.
What are the specific guidelines for this? Are there optimization reasons for one way or the other, or is one way just good practice? Are there any cases where it's acceptable to deviate from whatever the proper way of doing it is?
Declare variables as close to the first spot that you use them as possible. It's not really anything to do with efficiency, but makes your code much more readable. The closer a variable is declared to where it is used, the less scrolling/searching you have to do when reading the code later. Declaring variables closer to the first spot they're used will also naturally narrow their scope.
The proper way is to declare variables exactly when they are first used and minimize their scope in order to make the code easier to understand.
Declaring variables at the top of functions is a holdover from C (where it was required), and has absolutely no advantages (variable scope exists only in the source code, in the byte code all local variables exist in sequence on the stack anyway). Just don't do it, ever.
Some people may try to defend the practice by claiming that it is "neater", but any need to "organize" code within a method is usually a strong indication that the method is simply too long.
From the Java Code Conventions, Chapter 6 on Declarations:
6.3 Placement
Put declarations only at the beginning
of blocks. (A block is any code
surrounded by curly braces "{" and
"}".) Don't wait to declare variables
until their first use; it can confuse
the unwary programmer and hamper code
portability within the scope.
void myMethod() {
int int1 = 0; // beginning of method block
if (condition) {
int int2 = 0; // beginning of "if" block
...
}
}
The one exception to the rule is
indexes of for loops, which in Java
can be declared in the for statement:
for (int i = 0; i < maxLoops; i++) { ... }
Avoid local declarations that hide
declarations at higher levels. For
example, do not declare the same
variable name in an inner block:
int count;
...
myMethod() {
if (condition) {
int count = 0; // AVOID!
...
}
...
}
If you have a kabillion variables used in various isolated places down inside the body of a function, your function is too big.
If your function is a comfortably understandable size, there's no difference between "all up front" and "just as needed".
The only not-up-front variable would be in the body of a for statement.
for( Iterator i= someObject.iterator(); i.hasNext(); )
From Google Java Style Guide:
4.8.2.2 Declared when needed
Local variables are not habitually declared at the start of their
containing block or block-like construct. Instead, local variables are
declared close to the point they are first used (within reason), to
minimize their scope. Local variable declarations typically have
initializers, or are initialized immediately after declaration.
Well, I'd follow what Google does, on a superficial level it might seem that declaring all variables at the top of the method/function would be "neater", it's quite apparent that it'd be beneficial to declare variables as necessary. It's subjective though, whatever feels intuitive to you.
I've found that declaring them as-needed results in fewer mistakes than declaring them at the beginning. I've also found that declaring them at the minimum scope possible to also prevent mistakes.
When I looked at the byte-code generated by the location of the declaration few years ago, I found they were more-or-less identical. There were ocassionally differences depending on when they were assigned. Even something like:
for(Object o : list) {
Object temp = ...; //was not "redeclared" every loop iteration
}
vs
Object temp;
for(Object o : list) {
temp = ...; //nearly identical bytecoode, if not exactly identical.
}
Came out more or less identical
I am doing this very same thing at the moment. All of the variables in the code that I am reworking are declared at the top of the function. I've seen as I've been looking through this that several variables are declared but NEVER used or they are declared and operations are being done with them (ie parsing a String and then setting a Calendar object with the date/time values from the string) but then the resulting Calendar object is NEVER used.
I am going through and cleaning these up by taking the declarations from the top and moving them down in the function to a spot closer to where it is used.
Defining variable in a wider scope than needed hinders understandability quite a bit. Limited scope signals that this variable has meaning for only this small block of code and you can not think about when reading further. This is a pretty important issue because of the tiny short-term working memory that the brain has (it said that on average you can keep track of only 7 things). One less thing to keep track of is significant.
Similarly you really should try to avoid variables in the literal sense. Try to assign all things once, and declare them final so this is known to the reader. Not having to keep track whether something changes or not really cuts the cognitive load.
Principle: Place local variable declarations as close to their first use as possible, and NOT simply at the top of a method. Consider this example:
/** Return true iff s is a blah or a blub. */
public boolean checkB(String s) {
// Return true if s is a blah
... code to return true if s is a blah ...
// Return true if s is a blub. */
int helpblub= s.length() + 1;
... rest of code to return true is s is a blah.
return false;
}
Here, local variable helpblub is placed where it is necessary, in the code to test whether s is a blub. It is part of the code that implements "Return true is s is a blub".
It makes absolutely no logical sense to put the declaration of helpblub as the first statement of the method. The poor reader would wonder, why is that variable there? What is it for?
I think it is actually objectively provable that the declare-at-the-top style is more error-prone.
If you mutate-test code in either style by moving lines around at random (to simulate a merge gone bad or someone unthinkingly cut+pasting), then the declare-at-the-top style has a greater chance of compiling while functionally wrong.
I don't think declare-at-the-top has any corresponding advantage that doesn't come down to personal preference.
So assuming you want to write reliable code, learn to prefer doing just-in-time declaration.
Its a matter of readability and personal preference rather than performance. The compiler does not care and will generate the same code anyway.
I've seen people declare at the top and at the bottom of functions. I prefer the top, where I can see them quickly. It's a matter of choice and preference.

Categories