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 = "";
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.
I have used the below statement in my code to declare an empty string.
String temp = new String();
This has led to an issue raised by Sonarqube.
So what would be the efficient way to fix this ?
Is the below declaration a good way?
String temp = "";
Sonar is correct in that you shouldn't be using new String(). Initializing to empty string (String temp = "") is better. But if you do not use the value of empty string in any case, you should not initialize the variable to anything. You should only initialize a variable to a value you intend to use.
This is perfectly, and usually, acceptable:
String temp;
Your conditional logic should cover all cases of assignment.
/**
* Initializes a newly created {#code String} object so that it represents
* an empty character sequence. Note that use of this constructor is
* unnecessary since Strings are immutable.
*/
public String() {
this.value = new char[0];
}
Above souce code of String class depicts that use of this constructor is unnecessary. Every time you will create new object in heap. Better is to use String pool.
The second example is the correct one.
Use
String temp = "";
Yes, Sonar is correct. Using new String() is pretty much never a good idea.
The reason for this is, that the JVM caches Strings, so that you don't need to create a new object on the heap every time (this is why sometimes wrongly comparing strings with == works - They are both referring to the same cached instance on the heap).
Constructing a String yourself will circumvent that built-in cache and can lead to performance problems down the line. If all you need is an empty String just assign it:
String temp = "";
Or, if you just want to declare a String since you want to assign it later, just don't assign anything to it!
String temp;
if(condition()) {
temp = "hello!";
} else {
temp = "bye!";
}
If you plan on concatenating to your empty String in a loop though read this question about the attached performance issues and how to handle them.
I know that at compile time when a String is created, that String will be THE string used by any objects of that particular signature.
String s = "foo"; <--Any other identical strings will simply be references to this object.
Does this hold for strings created during methods at runtime? I have some code where an object holds a piece of string data. The original code is something like
for(datum :data){
String a = datum.getD(); //getD is not doing anything but returning a field
StringBuffer toAppend = new StringBuffer(a).append(stuff).toString();
someData = someObject.getMethod(a);
//do stuff
}
Since the String was already created in data, it seems better to just call datum.getD() instead of creating a string on every iteration of the loop.
Unless there's something I'm missing?
String instances are shared when they are the result of a compile-time constant expression. As a result, in the example below a and c will point to the same instance, but b will be a different instance, even though they all represent the same value:
String a = "hello";
String b = hell() + o();
String c = "hell" + "o";
public String hell() {
return "hell";
}
public String o() {
return "o";
}
You can explicitly intern the String however:
String b = (hell() + o()).intern();
In which case they'll all point to the same object.
The line
String a = datum.getD();
means, assign the result of evaluating datum.getD() to the reference a . It doesn't create a new String.
You are correct that strings are immutable so all references to the same string value use the same object.
As far as being static, I do not think Strings are static in the way you describe. The Class class is like that, but I think it is the only object that does that.
I think it would be better to just call the datum.getD() since there is nothing that pulling it out into its own sting object gains for you.
If you do use the datum.getD() several times in the loop, then it might make sense to pull the value into a String object, because the cost of creating a string object once might be less than the cost of calling the getD() function multiple times.
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.
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.