Which do you prefer and why"
String myString = null;
if(someCondition)
myString = "something";
else
myString = "something else";
OR
String myString = "";
if(someCondition)
myString = "something";
else
myString = "something else";
I know that using the ternary (? :) operator is possible but I'd like to know about the above two.
Neither. Instead, this:
String myString;
if (someCondition)
myString = "something";
else
myString = "something else";
In both of your alternatives, the variable is initialized with a value which will never ever be read. The fact that it's present at all is misleading.
I would actually use the conditional operator, of course - but barring that, the above is the better option.
The idiomatic way is to use ternary/conditional operator (JLS 15.25):
String myString = (someCondition ? "something" : "something else");
But you can also do the more verbose if-else statement if you really feel you must:
final String myString;
if(someCondition) {
myString = "something";
} else {
myString = "something else";
}
Note that I've added final modifier in the above snippet. If you're planning on further reassignments to the variable, then of course it can't be final, so you can remove the modifier and of course the code would still work.
Why final?
The point of the final in the above snippet is to show that the if-else construct will assign to myString once and exactly once in all possible execution paths. That is the main idea of the proposed if-else solution: if you're going to assign a value to a local variable only once, even if it can be one of several possibilities, then make it final to enhance readability.
Contrast that with this "alternative" proposal for example:
// DON'T DO THIS! Example only!
String myString = "something else";
if (someCondition) myString = "something";
With this construct, you may be assigning to myString twice, thus you couldn't put final here even if there was no further reassignment. You also couldn't put final in either of the original = null; or = ""; proposals, and this is one of the main reasons why they're not recommendable.
There's no point in assigning a value to a variable if you're just going to overwrite it before you're going to use it. It hurts readability, and may potentially even hide bugs, e.g. when one execution path fails to overwrite this "initial" value.
References
JLS 4.12.4 final Variables
JLS 16 Definite Assignment
Summary
Don't "initialize" a local variable just for the sake of doing it if you're going to overwrite it anyway
Let it be uninitialized, so that the compiler can help you identify a possible bug by pointing out any use of the variable while it's still uninitialized
If the code compiles, then the variable is assigned a "real" value at least once before all uses
If you don't need to reassign a local variable, make it final to enhance readability
final immediately assures readers that no further reassignments are possible
The compiler can help you prevent making the mistake of subsequent reassignment
If the code compiles, then the variable is assigned a "real" value exactly once before all uses
Generally speaking, you should let the compiler help you write the best, most readable code.
The initialization step is not necessary, and may confuse future readers.
My personal opinion is that this kind of variable should only be assigned once, hence it is a perfect candidate for the final keyword.
final String myString;
if (someCondition) {
myString = "something";
} else {
myString = "something else";
}
Note that the myString definition does not include an assignment (as this would prohibit later assignments) and that after the assignment it is read-only. This gives robust code and shows your intent more clearly.
Please also note that I believe in braces even for single lines. Probably a Perl habit, but if you don't, it will bite you someday.
String myString = "something else";
if(someCondition) myString = "something"; // (use curly braces if you prefer)
I prefer first one, because String myString = "" will create additional object in the pool
String mystring = null;
mystring.length()
// Cause error
Above will cause error due to null pointer.
string myString = new String();
myString.length()
// will not cause error
I like to use later, but I think it's personal preference.
How about this follwing code ,anyways he wants to set something.
String myString = (someCondition) ? "something " : "else something";
or this
String myString = "else something";
if (someCondition)
myString = "something";
in the above case , if you are 90% sure that someCondition is always true. otherwise unnecessary object creation in declaration.Expecting comments from Gurus.
Related
There is a method:
private String myMethod(String gender)
{
String newString = "";
if(gender.equals("a"))
newString = internal.getValue();
else
newString = external.getValue();
return newString;
}
I refactored everything, but with one small change:
String newString; instead of: String newString = "";
Does this refactor improve the code? I know that String is null when we don't initialize it, but in this example it always will have value a from if or else. Does this refactor change anything?
To answer the direct question: there's no need to assign a value initially here; all branches of the code's execution will pan out to giving newString a value. Thus you don't need to initialize it at all. Otherwise, I would initialize to whatever you would want as a "default" value.
Instead of two returns or a branching statement to assign a variable, I would just return with a ternary:
private String myMethod(String gender) {
return gender.equals("a")
? internal.getValue()
: external.getValue();
}
Is it better to initialize String or to leave it as null?
Your premise is flawed: not initializing the String doesn't mean its value is null.
You are not allowed to use a local variable before it has been assigned, in order to avoid you accidentally using a value you didn't intend. As such, the value isn't "null", it's undefined (*).
This is called definite assignment checking, and is there to prevent certain types of bug. If you give the variable a value you don't need to give it, you disable this check, and so are open to the bugs the compiler was trying to protect you from.
For example, if the code looked like this:
private String myMethod(String gender)
{
String newString = "";
if(gender.equals("a"))
newString = internal.getValue();
else if (gender.equals("b");
newString = external.getValue();
// Oops! meant to check if gender.equals("c")
return newString;
}
you might have a bug, because there is a missing case that you haven't checked.
If you had explicitly assigned null to the variable, you would have much the same issue; but now your method would return null, and so possibly cause an NPE in the calling code.
If you had omitted the = "", the compiler would stop you using newString in the return.
(Assigning and reassigning the variable also means the variable would not be effectively final, so you would be unable to use it inside a lambda or anonymous class).
(*) This only applies to local variables, and final member/static variables. Class members do not have to be definitely assigned before use if they are not final, which is a rich seam for bugs, and a good reason to make class members final wherever possible. And, technically, final members are initialized to their type's default value first, so you can actually read them as null before they are initialized.
It's best to only initialize a String (or anything else) if there is a scenario in which the initial value is used.
In your case you have assigned newString to a string literal that serves no purpose but to confuse the reader.
It should be evident that the performance and functionality will not change in any relavent way.
My take on the shortest form without the terniary operator (which I think decreases readability) :
private String myMethod(String gender)
{
if(gender.equals("a"))
return internal.getValue();
return external.getValue();
}
I would probably have a full if {...} else {...} construct like the other answers in my own code.
Also not all debuggers can easily show what is being returned from a method as part of the normal flow, so it may be easier if the return value is captured in a variable and THEN returned (where the breakpoint can be put on the return statement)
You can make this string final and keep unassigned to be sure that all if branches assigns the value:
final String result;
if (condition1) {
result = "one";
} else if (condition2) {
result = "two";
} else {
result = "other";
}
return result;
With this approach compiler will check that result variable was assigned once in each branch. It may be helpful if you add one more condition branch, or if you try to overwrite variable by mistake - compiler will fail and show the error.
In your case (if else condition) there no need to initialize the String, you can simple put it as String newString; and that would be ok because either way, at the end it will have a different value.
private String myMethod(String gender)
{
String newString;
if(gender.equals("a"))
newString = internal.getValue();
else
newString = external.getValue();
// Missing return statement.
}
Also, I see that you have a function that returns a string. Assuming that the newString variable is what you will be returning, instead of creating a string variable you can simple return the string in the condition:
private String myMethod(String gender)
{
if(gender.equals("a"))
return internal.getValue();
else
return external.getValue();
}
My colleges are right, this can be done with the tenary operator.
In addition I thinks its very important to prevent NullPoiterExeptions as often as possible.
What if gender would be null? NullPointerException
I would switch "a" and gender like this:
private String myMethod(String gender) {
return "a".equals(gender)
? internal.getValue()
: external.getValue();
}
According to Java doc:
Default Values
It's not always necessary to assign a value when a field is declared. Fields that are declared but not initialized will be set to a reasonable default by the compiler. Generally speaking, this default will be zero or null, depending on the data type. Relying on such default values, however, is generally considered bad programming style.
Local variables are slightly different; the compiler never assigns a default value to an uninitialized local variable. If you cannot initialize your local variable where it is declared, make sure to assign it a value before you attempt to use it. Accessing an uninitialized local variable will result in a compile-time error.
This question already has answers here:
Variable might not have been initialized error
(12 answers)
Closed 5 years ago.
I have the below scenario.
I would like to use a variable inside lambda expression. But this variable will be have a onetime value(final) based on a condition.
final String constraintsAmount;
if(constraint.isPresent()) {
constraintsAmount = constraint.getValue();
}
After this I start iterating over a list using forEach + lambda expression.
Now I have to use this constraintsAmount field inside this iteration.
But it says that "constraintsAmount might not have been initialized".
How can I get around this one.
Note :
I don't want to declare this variable as an instance variable and I certainly don't want to declare and initialize this variable inside the iteration.
Since it is a final I cant initialize it and then reuse it inside the if check. So wanted to check what is the work around.
From a compiler perspective you need an else block:
final String constraintsAmount;
if(constraint.isPresent()) {
constraintsAmount = constraint.getValue();
} else {
constraintsAmount = ...
}
Even better the ternary operator:
final String constraintsAmount = constraint.isPresent() ? constraint.getValue() : ...
NOTE: In Java 8 you don't need to declare a variable final to use it in a "closure" (inside lambda stuff).
Or maybe you might need to redesign your code altogether. Hard to tell as I don't have enough information.
Assumimg constraint is an Optional<String> you could do the following:
String constraintsAmount = constraint.orElse(<somedefault>);
Otherwise if you only want to do something if constraint has a value do the following:
constraint.ifPresent(constraintsAmount -> { .... });
A local variable has to be explicitly initialized before being used.
Your problem is not specific to the final modifier or the use of a lambda expression.
If this condition is true : if(constraint.isPresent()) {,
constraintsAmount is initialized. But in the contrary case, it is not initialized.
So if you use this variable, the compiler doesn't accept that.
This code illustrates the problem :
final String constraintsAmount;
if(constraint.isPresent()) { // initialized
constraintsAmount = constraint.getValue();
}
// if the conditional statement is false, the constraintsAmount var is not initialized
// So the compiler generates the error
// constraintsAmount might not have been initialized
String lowerCaseString = constraintsAmount.toLowerCase();
Either, value constraintsAmount in an else statement.
Or remove final to give a default value to the variable.
New to Java, sorry if this is a stupid question. I'd like to assign new values to a couple of variables from inside a while loop, but that's not working. I'm getting an error "The local variable newString may not have been initialized" when trying to compile. Here's an example:
public class Test {
public static String example() {
String first;
String second;
String newString;
int start = 1;
while (start<5) {
if (start<4) {
first = "hello";
second = "world";
newString = first + second;
}
start += 1;
}
return newString;
}
public static void main(String[] args) {
System.out.println(example());
}
}
When you are returning a variable as a result from a method, the compiler needs to be sure that a value would be assigned to that variable (in your case newString).
Although it is pretty clear for you that the condition start < 4 will be true at some points, the compiler is not intelligent enough to figure that out, which means you have to return only variables, which has values for sure.
Depending on the purpose of your method, you have the following opportunities:
String newString = "";
In this case you are sure that your method will never return null, which could be tricky in some cases for finding errors.
Another opportunity is
String newString = null;
if you want to allow your method to return null values.
As it is obvious in this case that you will eventually enter the if-block and assign a value to the variable newString in other cases it won't be that obvious, so you need to determine whether to allow your method return null values or not.
You are getting this error because the compiler does not know if newString will ever be initialized before it gets returned.
Although you know that both start<5 and start<4 are true and will, thus, execute, the compiler doesn't perform these types of calculations during compilation. Hence, for all it knows, those statements will never execute, and thus newString may get returned before it is initialized.
Hence, you should initialize it (e.g. to null) when you declare it to avoid this error.
There is a rule in Java that all local variables must be initialized before they are first read. You are using newString as a return value, which is a read operation. Although you are assigning a value to newString, it is done in conditional situation (start<5 && start<4). At the compile time, the compiler does not know what will be the result of running the code and it conservatively complains about this situation.The simple solution will be initializing the string:
String newString = "";
I saw a few old code snippets in a piece of software that no one remembers who wrote that instead of doing something like:
String abc = SOME_CONSTANT.toLowerCase()
They do:
String abc = new String(SOME_CONSTANT).toLowerCase()
I can't see any value in this - seems like plain old bad programming (e.g. not understanding that String is immutable). Anyone can see a good reason for this?
Note: SOME_CONSTANT is defined as -
public static final String SOME_CONSTANT = "Some value";
No, it just creates more objects (unless the compiler optimizes it away)
The only point in wrapping a String inside another String is to force a copy. e.g.
String str = "A very very log string .......";
// uses the same underlying string which we might not need after this.
String str1 = str.substring(0, 1);
// take a copy of which only has one char in it.
String str2 = new String(str1);
I would just do
public static final String SOME_CONSTANT = "Some value";
public static final String LOWER_CONSTANT = SOME_CONSTANT.toLowerCase();
I agree with you: it's bad programming
No good reason. As you said, String is immutable, so calling toLowerCase() on it will always produce a new string anyway.
new String(someString)
only makes sense in one important case:
String s = incredilyLongString.substring(1000,1005);
String t = new String(s);
assume incredilyLongString is 1000000 chars long (e.g. an XML file) and you just want 5 chars of it. The String s will still take at least ONE MEGABYTE of memory, but String t will be created from scratch and so will take up only the necessary memory.
I'm not sure, but I think when you use new String() you force the JVM to create a new object for that String. If you use SOME_CONSTANT.toLowerCase() JVM will search in the pool of Strings and just make a reference if there is a same String.
Maybe use new String() in this case can be a good practice just to makes clear that the toLowerCase() will affect just the new String generated, not the constant.
But anyway, the effect is the same
In Java what is the best practice of initializing the private string members
class A {
private String mem = ??//
private int num;
A (int a) {
this.num = a;
}
A (int a, String s) {
this.num = a;
this.mem = s; //What if s is null (Is this a good practice)
}
}
Totally depends on what you are doing...
Ask yourself these questions (whenever you make any variable)
will the value ever change?
what are the allowed valued?
In this case if "mem" and "num" will never change then mark them as final (you can always take away the final later if you find you will have to change them).
Also in this case can "mem" ever be null? Can num ever be < 0?
Here is an example of the changes I might make:
class A
{
private final String mem;
private int num;
A (final int a)
{
this(a, null);
}
A (final int a,
final String s)
{
if(a < 0)
{
throw new IllegalAgumentException("a cannot be < 0, was: " + a);
}
num = a;
mem = s;
}
}
In short there is no best practice.
Depends on your model. Some strings may need to be nullable and others (mandatory) should always contain a value. Sometimes more than just an empty string.
I wouldn't blindly allocate the empty string to everything.
Leave it blank: private String mem;
With regard to "What if s is null", one should make it a point to construct all objects in a valid state. But what is "valid" depends completely on your application. When the rest of your program references mem and finds it to be null, what will happen? Would it be better to have it be an empty string or some other default value? It varies depending on the circumstances.
There is a trick with this (and most variables, actually).
If you NEVER initialize the variable, you are actually giving the compiler more information.
In some cases you have to, but this case is really interesting:
String s;
if(a==5)
s="It was Five";
if(a==6)
s="It was Six";
System.out.print(s);
This is very bad code, but the neat thing is, Java will catch it and tell you "Hey, there is a good chance that you'll hit that print with a null".
If you had started with either of these two lines:
String s=null;
String s="";
The compiler couldn't have helped you with that mistake. (It's not likely that you meant to print "", is it?)
The correct fix when you see this "variable might not have been initialized" message is NOT to initialize the string to null or "". It's to actually be sure s is set to a valid value in every possible path:
if(a==5)
s="It was Five";
else if(a==6)
s="It was Six";
else
s="It was something else, who knows?";
This will compile fine, even without initializing s. This works on complex if/else, looping and exception cases, so don't just assume it will only catch trivial cases like this!
So "Best Practice" is to NEVER initialize variables to "", 0 or null. Don't initialize them unless it really makes sense and you are using the value you set it to in some way.
Once you get the error in my example, instead of the else you might just initialize s to "It was something else, who knows?" instead of adding the else--that would be perfectly valid; but by not initializing it to null or "" as a matter of habit, at least it was able to remind you that you had to do so.
It depends on the usage of that attribute.
Is a null a valid input in your program?
If it's not, then you should validate it and throw a Npe.
A ( int a, String s ) {
if( s == null ) {
throw new NullPointerException("The value of s cannot be null");
}
....
}
If it is a valid value, then there won't be a problem.
It is a good practice though that you assign the value only in one place, so you don't get surprises if you validate in the constructor, but later a setter change the value.
If the String is never supposed to be null, then the constructor that sets it from a parameter should throw an Exception if the parameter is null, and the other constructor should set it to a useful default value.
If the String is never supposed to be null, you can declare it final, and this way the compiler makes sure that all constructors assign a value to it.
class A {
private final String mem;
private int num;
A (int a) {
this.num = a; // <= compile error, mem must be assigned
}
A (int a, String s) {
this.num = a;
this.mem = s; // <= you need to null-check yourself
}
}
It is still up to the constructors to make sure that they don't just assign null.
Maybe the future will bring a #NotNull annotation...
You should define a contract and then code to it. If you allow the member to be null, then initialize it to null. If not, usually initializing to empty string "" is a good idea. But again, it depends on your data model, sometimes empty string is invalid too.
In all cases, your constructor should throw NullPointerException or IllegalArgumentException if an invalid string is passed in.