views:

469

answers:

9

This might sound stupid, but why doesn't the Java compiler warn about the expression in the following if statement:

String a = "something";
if(a == "something"){
  System.out.println("a is equal to something");
}else{
  System.out.println("a is not equal to something");
}

I realize why the expression is untrue, but AFAIK, a can never be equal to the String literal "something". The compiler should realize this and at least warn me that I'm an idiot who is coding way to late at night.

Clarification This question is not about comparing two String object variables, it is about comparing a String object variable to a String literal. I realize that the following code is useful and would produce different results than .equals():

String a = iReturnAString();
String b = iReturnADifferentString();
if(a == b){
  System.out.println("a is equal to b");
}else{
  System.out.println("a is not equal to b");
}

In this case a and b might actually point to the same area in memory, even if it's not because of interning. In the first example though, the only reason it would be true is if Java is doing something behind the scenes which is not useful to me, and which I can't use to my advantage.

Follow up question Even if a and the string-literal both point to the same area in memory, how is that useful for me in an expression like the one above. If that expression returns true, there isn't really anything useful I could do with that knowledge, is there? If I was comparing two variables, then yes, that info would be useful, but with a variable and a literal it's kinda pointless.

+13  A: 

Actually they can indeed be the same reference if Java chooses to intern the string. String interning is the notion of having only one value for a distinct string at runtime.

Java notes about string interning

JaredPar
They *can* be, but there's no guarantee if I understand correctly.
Michael Angstadt
it is certain that they are the same, by spec.
Bozho
@Bozho, it's been a _long_ time since I looked deeply at this issue. But I believe at one point of time the result of the code as typed in the question produced different behavior depending on which compiler + runtime was used. Things certainly could have changed in the 7+ years since I looked at this.
JaredPar
well, at least that's what the javadocs say about `String#intern()`:"It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true. "
Bozho
It might be certain by the spec if they're both string literals (as in this case), but it definitely isn't certain in the general case. Two strings can be `equal()` but not `==` if you constructed them yourself with `StringBuilder` or by any means other than an exact literal.
Jonathan M Davis
if you interned them, they are `==`
Bozho
@Bozho So, basically, if you put them in the string pool yourself with `intern()`, then they work with `==`. However, there's no guarantee that they'll put themselves in the string pool.
Jonathan M Davis
There is, in case they are declared with the string literal (inline, or as constants)
Bozho
Also, if you do something like this, then == will not return true:String a = "foo";String b = new String("foo");
Michael Angstadt
and doing `new String("foo")` is a bad practice
Bozho
+1  A: 

The compiler doesn't care that you're trying to do identity comparison against a literal. It could also be argued that it's not the compiler's job to be a code nanny. Look for a lint-like tool if you want to catch situations like this.

Ignacio Vazquez-Abrams
+2  A: 

Actually, it may sometimes be true, depending on if Java takes an existing String from its internal String cache, creating the first declaration and then storing it, or taking it for both string declarations.

Chris Dennett
+3  A: 

Because:

  • you might actually want to use ==, if working with constants and interned strings
  • the compiler should make an exception only for String, and no other type. What I mean is - whenever the compiler encounters == it should check if the operands are Strings in order to issue a warning. What if the arguments are Strings, but are referred to as Object or CharSequence ?

The rationale given by checkstyle for issuing an error is that novice programmers often do this. And if you are novice, I'd be hard to configure checkstyle (or pmd), or even to know about them.

Another thing is the actual scenario when strings are compared and there is a literal as one of the operands. First, it would be better to use a constant (static final) instead of a literal. And where would the other operand come from? It is likely that it will come from the same constant / literal, somewhere else in the code. So == would work.

Bozho
The compiler has already made an exception for String, it is the only object I can use operators (like + and "") on. Also, I'm not suggesting that == should do the same task as equals, just that the compiler could warn me when I'm making a useless comparison
Marius
I expanded my explanation.
Bozho
I think "==" with any string literal should throw a warning. unless you're doing some hard-core hacking and know what you're doing, you're going to want to .equals(). And if you are hard-core hacking, then why not add a flag -Wignore-string-equals or smth.
Claudiu
If you are comparing strings where one of them is a literal, the other should also be defined as a literal or preferably - constant somewhere. And in that case `==` is OK.
Bozho
One side is still a string literal, so it doesn't matter what the other side is. But I see your point about making exceptions; the compiler warning code could get really hairy.
Marius
it does matter - if the other side is an interned string, `==` would work perfectly fine.
Bozho
Bozho, I realise it would work perfectly fine, that does not mean it adds any value to your code. When would you ever want to know if a String literal has been interned? What would you do with such information? Even if it would be useful, in 99.9% of the time it would be a mistake made by the programmer, and so the compiler could atleast warn him.
Marius
+7  A: 

Compiler warnings tend to be about things that are either blatantly wrong (conditionals that can never be true or false) or unsafe (unchecked casts). The use of == is valid, and in some rare cases intentional.

I believe all of Checkstyle, FindBugs and PMD will warn about this, and optionally a lot of other bad practices we tend to have when half asleep or otherwise incapacitated ;).

Angelo Genovese
well, the (eclipse) compiler also warns you about `=` in `if`-clauses, but in some cases this might be intentional, too.
Bozho
Could you make the tools names in **bold**, please (or even better: make a list with links...)?
Henning
@Henning - Sorry, relatively new to SO@Bart K. - Thanks :)
Angelo Genovese
+2  A: 

Depending on the context, both identity comparisons and value comparisons can be legitimate.

I can think of very few queries where there is a deterministic automated algorithm to figure out unambiguously that one of them is an error.

Therefore, there's no attempt to do this automatically.

If you think about things like caching, then there are situations where you would want to do this test.

Uri
A: 

There are cases where you actually care whether you're dealing with exactly the same object rather than whether two objects are equal. In such cases, you need == rather than equals(). The compiler has no way of knowing whether you really wanted to compare the references for equality or the objects that they point to.

Now, it's far less likely that you're going to want == for strings than it would be for a user-defined type, but that doesn't guarantee that you wouldn't want it, and even if it did, that means that the compiler would have to special case strings are specifically check to make sure that you didn't use == on them.

In addition, because strings are immutable, the JVM is free to make string which would be equal per equals() share the same instance (to save memory), in which case they would also be equal per ==. So, depending on what the JVM does, == could very well return true. And the example that you gave is actually one where there's a decent chance of it because they're both string literals, so it would be fairly easy for the JVM to make them the same string, and it probably would. And, of course, if you want to see whether the JVM is making two strings share the same instance, you would have to use == rather than equals(), so there's a legitimate reason to want to use == on strings right there.

So, the compiler has no way of knowing enough of what you're doing to know that using == instead of equals() should be an error. This can lead to bugs if you're not careful (especially if you're used to a language like C++ which overloads == instead of having a separate equals() function), but the compiler can only do so much for you. There are legitimate reasons for using == instead of equals(), so the compiler isn't going to flag it as an error.

Jonathan M Davis
A: 

There exist tools that will warn you about these constructs; feel free to use them. However there are valid cases when you want to use == on Strings, and it is much worse language design to warn a user about a perfectly valid construct than to fail to warn them. When you have been using Java a year or so (and I will bet good money that you haven't reached that stage yet) you will find avoiding constructs like this is second nature.

DJClayworth
A: 

"I realize why the expression is untrue, but AFAIK, a can never be equal to the String literal "something"."

To clarify, in the example given, the expersion is always TRUE and a can be == and equals() to the String literal and in the example given it is always == and equals().

It is ironic that you appear have given the rare counter example to your own argument.

Peter Lawrey