Sign up ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free.

Every competent Java programmer knows that you need to use String.equals() to compare a string, rather than == because == checks for reference equality.

When I'm dealing with strings, most of the time I'm checking for value equality rather than reference equality. It seems to me that it would be more intuitive if the language allowed string values to be compared by just using ==.

As a comparison, C#'s == operator checks for value equality for strings. And if you really needed to check for reference equality, you can use String.ReferenceEquals.

Another important point is that Strings are immutable, so there is no harm to be done by allowing this feature.

Is there any particular reason why this isn't implemented in Java?

share|improve this question
You might want to look at Scala, where == is object equality and eq is reference equality (…). – Giorgio Apr 2 '13 at 11:12
Just as a note, and this may not help you, but as far as I remember, you can compare string literals with an '==' – Kgrover Apr 2 '13 at 16:07
@Kgrover: you can, but that's just a convenient by-product of reference equality and how Java aggressively optimizes string matching literals into references to the same object. In other words, it works, but for the wrong reasons. – tdammers Apr 2 '13 at 19:29
@tdammers Yep, I agree. It was just a point I was trying to make. – Kgrover Apr 2 '13 at 20:13
It's a consequence of design decisions which make a lot of sense in many other scenarios. But since you seem to know that and be asking this anyways, I feel compelled to counter your question: Why does there have to be a use case for String reference comparison? – Jacob Raihle Oct 20 at 15:23

7 Answers 7

up vote 75 down vote accepted

I guess it's just consistency, or "principle of least astonishment". String is an object, so it would be surprising if was treated differently than other objects.

At the time when Java came out (~1995), merely having something like String was total luxury to most programmers who were accustomed to representing strings as null-terminated arrays. String's behavior is now what it was back then, and that's good; subtly changing the behavior later on could have surprising, undesired effects in working programs.

As a side note, you could use String.intern() to get a canonical (interned) representation of the string, after which comparisons could be made with ==. Interning takes some time, but after that, comparisons will be really fast.

Addition: unlike some answers suggest, it's not about supporting operator overloading. The + operator (concatenation) works on Strings even though Java doesn't support operator overloading; it's simply handled as a special case in the compiler, resolving to StringBuilder.append(). Similarly, == could have been handled as a special case.

Then why astonish with special case + but not with ==? Because, + simply doesn't compile when applied to non-String objects so that's quickly apparent. The different behavior of == would be much less apparent and thus much more astonishing when it hits you.

share|improve this answer
Special cases add astonishment. – Blrfl Apr 2 '13 at 21:13
Strings were a luxury in 1995? Really?? Look at the history of computer languages. The number of languages that had some type of string at the time would far outnumber those that did not. How many languages besides C and it's descendents used null terminated arrays? – WarrenT Apr 3 '13 at 0:27
@WarrenT: Sure, some (if not most) languages had some type of string, but Unicode-capable, garbage-collected strings were a novelty in 1995, I think. For example, Python introduced Unicode strings with version 2.0, year 2000. Choosing immutability was also a controversial choice at that time. – Joonas Pulakka Apr 3 '13 at 6:14
@JoonasPulakka Then maybe you should edit your answer to say that. Because as it stands, the “total luxury” part of your answer is quite wrong. – svick Apr 3 '13 at 18:45
Interning has a cost: you get a string that will never ever be deallocated. (Well, not unless you use your own interning engine that you can throw away.) – Donal Fellows Apr 3 '13 at 19:10

Consistency within the language. Having an operator that acts differently can be surprising to the programmer. Java doesn't allow users to overload operators - therefore reference equality is the only reasonable meaning for == between objects.

Within Java:

  • Between numeric types, == compares numeric equality
  • Between boolean types, == compares boolean equality
  • Between objects, == compares reference identity
    • Use .equals(Object o) to compare values

That's it. Simple rule and simple to identify what you want. This is all covered in section 15.21 of the JLS. It comprises three subsections that are easy to understand, implement, and reason about.

Once you allow overloading of ==, the exact behavior isn't something that you can look to the JLS and put your finger on a specific item and say "that's how it works," the code can become difficult to reason about. The exact behavior of == may be surprising to a user. Every time you see it, you have to go back and check to see what it actually means.

Since Java doesn't allow for overloading of operators, one needs a way to have a value equality test that you can override the base definition of. Thus, it was mandated by these design choices. == in Java tests numeric for numeric types, boolean equality for boolean types, and reference equality for everything else (which can override .equals(Object o) to do whatever they want for value equality).

This is not an issue of "is there a use case for a particular consequence of this design decision" but rather "this is a design decision to facilitate these other things, this is a consequence of it."

String interning, is one such example of this. According to the JLS 3.10.5, all string literals are interned. Other strings are interned if one invokes .intern() on them. That "foo" == "foo" is true is a consequence of design decisions made to minimize the memory footprint taken up by String literals. Beyond that, String interning is something that is at the JVM level that has a little bit of exposure to the user, but in the overwhelming vast majority of cases, should not be something that concerns the programmer (and use cases for programmers wasn't something that was high on the list for the designers when considering this feature).

People will point out that + and += are overloaded for String. However, that is neither here nor there. It remains the case that if == has a value equality meaning for String (and only String), one would need a different method (that only exists in String) for reference equality. Furthermore, this would needlessly complicate methods that take Object and expect == to behave one way and .equals() to behave another requiring users to special case all those methods for String.

The consistent contract for == on Objects is that it is reference equality only and that .equals(Object o) exists for all objects which should test for value equality. Complicating this complicates far too many things.

share|improve this answer
thanks for taking the time to answer. This would be a great answer on the other questions that I linked to. Unfortunately, this is not suitable for this question. I'll update the OP with clarifications based on the comments. I'm looking more for the use cases where a language-user would want to have the false-negatives when comparing strings. The language provides this feature as consistency, now I would like us to go a step further. Perhaps thinking of this from the new-language designer, is it needed? (unfortunately, no lang-design.SE) – Anonsage Oct 19 at 20:52
@Anonsage its not a false negative. They aren't the same object. That is all it is saying. I must also point out that in Java 8, new String("foo") == new String("foo") may be true (see String Deduplication). – MichaelT Oct 19 at 20:52
As to language design, CS.SE advertises that it may be on topic there. – MichaelT Oct 19 at 20:58
ooh, thank you! I will post my future lang-design questions there. :) And, yeah, unfortunately 'false-negative' isn't the most accurate way to describe my question and what I'm looking for.. I need to write more words so people don't have to guess what I'm trying to say. – Anonsage Oct 19 at 21:03
"Consistency within the language" also helps with generics – Brendan Oct 20 at 14:22

Java seems to have been designed to uphold a fundamental rule that the == operator should be legal any time one operand can be converted to the type of the other, and should compare the result of such conversion with the non-converted operand.

This rule is hardly unique to Java, but it has some far-reaching (and IMHO unfortunate) effects on the design of other type-related aspects of the language. It would have been cleaner to specify the behaviors of == with regard to particular combinations of operand types, and forbid combinations of types X and Y where x1==y1 and x2==y1 wouldn't imply x1==x2, but languages seldom do that [under that philosophy, double1 == long1 would either have to indicate whether double1 is not an exact representation of long1, or else refuse to compile; int1==Integer1 should be forbidden, but there should be a convenient and efficient non-throwing means of testing whether an object is a boxed integer with particular value (comparison with something that isn't a boxed integer should simply return false)].

With regard to applying the == operator to strings, if Java had forbidden direct comparisons between operands of type String and Object, it could have pretty well avoided surprises in the behavior of ==, but there's no behavior it could implement for such comparisons that wouldn't be astonishing. Having two string references kept in type Object behave differently from references kept in type String would have been far less astonishing than having either of those behaviors differ from that of a legal mixed-type comparison. If String1==Object1 is legal, that would imply that the only way for the behaviors of String1==String2 and Object1==Object2 to match String1==Object1 would be for them to match each other.

share|improve this answer
I must be missing something, but IMHO == on objects should simply call (null-safe) equals and something else (e.g., === or System.identityEqual) should be used for the identity comparison. Mixing primitives and objects would be initially forbidden (there was no autoboxing before 1.5) and then some simple rule could be found (e.g. null-safe unbox, then cast, then compare). – maaartinus Nov 17 '14 at 8:33
@maaartinus: A good language design should use separate equality operators for value and reference equality. While I agree that conceptually it would have been possible to have an int==Integer operator return false if the Integer is null, and otherwise compare values, that approach would have been unlike the behavior of == in all other circumstances, where it unconditionally coerces both operands to the same type before comparing them. Personally I wonder if auto-unboxing was put in place in an effort to allow int==Integer to have a behavior that wasn't nonsensical... – supercat Nov 17 '14 at 16:16
...since autoboxing the int and doing a reference comparison would have been silly [but wouldn't always fail]. Otherwise, I see no reason to allow an implicit conversion that can fail with an NPE. – supercat Nov 17 '14 at 16:20
I think that my idea is consistent. Just keep it mind that in the better world, == has nothing to do with identityEquals. +++ "separate equality operators for value and reference equality" - but which ones? I'd consider both primitive == and equals as doing value comparison in the sense that equals looks at the value of the reference. +++ When == meant equals, then int==Integer SHOULD do autoboxing and compare the references using null-safe equals. +++ I'm afraid, my idea is not really mine, but just what Kotlin does. – maaartinus Nov 18 '14 at 2:04
@maaartinus: If == never tested reference equality, then it could sensibly perform a null-safe value-equality test. The fact that it does test reference equality, however, severely limits how it can handle mixed reference/value comparisons without inconsistency. Note also that Java is fixed on the notion that operators promote both operands to the same type, rather than yielding special behaviors based upon the combinations of types involved. For example, 16777217==16777216.0f returns true because it performs a lossy conversion of the first operand to float, while a... – supercat Nov 18 '14 at 4:54

This has been made different in other languages.

In Object Pascal (Delphi/Free Pascal) and C#, the equality operator is defined to compare values, not references, when operating on strings.

Particularly in Pascal, string is a primitive type (one of the things I really love about Pascal, getting NullreferenceException just because of an uninitialized string is simply irritating) and have copy-on-write semantics thus making (most of time) string operations very cheap (in other words, only noticeable once you start concatenating multi-megabyte strings).

So, it's a language design decision for Java. When they designed the language they followed the C++ way (like Std::String) so strings are objects, which is IMHO an hack to compensate of C lacking an real string type, instead of making strings an primitive (which they are).

So for a reason why, I can only speculate they made that to easy on their side and not coding the operator make an exception on compiler to strings.

share|improve this answer
how does this answer the question asked? – gnat Apr 3 '13 at 12:53
See last sentence (which I separated in a proper paragraph in a edit). – Fabricio Araujo Apr 3 '13 at 18:23
IMHO, String should have been a primitive type in Java. Unlike other types, the compiler needs to know about String; further, operations on it will be sufficiently common that for many kinds of application they may pose a performance bottleneck (which could be eased by native support). A typical string [lowercase] would have an object allocated on the heap to hold its contents, but no "normal" reference to that object would exist anywhere; it could thus be a single-indirected Char[] or Byte[] rather than having to be a Char[] indirected through another object. – supercat Feb 25 '14 at 13:27

James Gosling, the creator of Java, explained it this way back in July 2000:

I left out operator overloading as a fairly personal choice because I had seen too many people abuse it in C++. I've spent a lot of time in the past five to six years surveying people about operator overloading and it's really fascinating, because you get the community broken into three pieces: Probably about 20 to 30 percent of the population think of operator overloading as the spawn of the devil; somebody has done something with operator overloading that has just really ticked them off, because they've used like + for list insertion and it makes life really, really confusing. A lot of that problem stems from the fact that there are only about half a dozen operators you can sensibly overload, and yet there are thousands or millions of operators that people would like to define -- so you have to pick, and often the choices conflict with your sense of intuition.

share|improve this answer
Ah, yes, the old "lets' blunt the pointy tool so the oafs won't hurt themselves" excuse. – Blrfl Apr 2 '13 at 11:38
@Blrfl: If a tool creates more problems than it solves it is not a good tool. Of course, deciding whether this is the case with operator overloading could turn into a very long discussion. – Giorgio Apr 2 '13 at 11:43
-1. This doesn't answer the question at all. Java does have operator overloading. The == operator is overloaded for objects and primitives. The + operator is overloaded for byte, short, int, long, float, double, String and probably a couple of others I forgot. It would have been perfectly possible to overload == for String as well. – Jörg W Mittag Apr 2 '13 at 13:04
@Jorg - no it does not. Operator overloading is impossible to define at the user level. There are indeed some special cases in the compiler but that hardly qualifies – AZ01 Apr 2 '13 at 14:58
@Blrfl: I don't mind the oafs hurting themselves. It's when they accidentially poke my eye out that I get annoyed. – Jonas Apr 2 '13 at 21:21

Java doesn't support operator overloading, which means == only applies to primitive types or references. Anything else requires invocation of a method. Why the designers did this is a question only they can answer. If I had to guess, it's probably because operator overloading brings complexity they weren't interested in adding.

I'm no expert in C#, but the designers of that language appear to have set it up such that every primitive is a struct and every struct is an object. Because C# allows operator overloading, that arrangement makes it very easy for any class, not just String, to make itself work in the "expected" way with any operator. C++ allows the same thing.

share|improve this answer
"Java doesn't support operator overloading, which means == only applies to primitive types or references. Anything else requires invocation of a method.": One could add that if == meant string equality, we would need another notation for reference equality. – Giorgio Apr 2 '13 at 11:03
@Giorgio: Exactly. See my comment on Gilad Naaman's answer. – Blrfl Apr 2 '13 at 11:05
Although that can be solved by a static method that compares the references of two objects (or an operator). Like in C#, for example. – Gilad Naaman Apr 2 '13 at 11:08
@GiladNaaman: That would be a zero-sum game because it causes the opposite problem to what Java has now: equality would be on an operator and you'd have to invoke a method to compare references. Further, you'd have to impose the requirement that all classes implement something that can be bound to ==. That's effectively adding operator overloading, which would have tremendous implications on the way Java is implemented. – Blrfl Apr 2 '13 at 11:16
@Blrfl: Not really. There will always be a defined way to compare reference (ClassName.ReferenceEquals(a,b)), and a default == operator and Equals method both pointing to ReferenceEquals. – Gilad Naaman Apr 2 '13 at 11:29

In Java, there is no operator overloading whatsoever, and that's why the comparison operators are only overloaded for the primitive types.

The 'String' class is not a primitive, thus it does not have an overloading for '==' and uses the default of comparing the address of the object in the computer's memory.

I'm not sure, but I think that in Java 7 or 8 oracle made an exception in the compiler to recognize str1 == str2 as str1.equals(str2)

share|improve this answer
"I'm not sure, but I think that in Java 7 or 8 oracle made an exception in the compiler to recognize str1 == str2 as str1.equals(str2)": I would not be surprised: Oracle seems to be less concerned with minimalism than Sun was. – Giorgio Apr 2 '13 at 10:55
If true, that's a very ugly hack since it means there's now one class the language treats differently from all others and breaks code that compares references. :-@ – Blrfl Apr 2 '13 at 11:02
@Blrfl: Yes, I agree, but I'm not sure that that's true. Just something from the top of my head. I'm searching for a source. – Gilad Naaman Apr 2 '13 at 11:04
@Brlfl: While that is true, that sounds like an exceptionally bad thing to rely on in the first place, as strings are immutable, internable objects. – Williham Totland Apr 2 '13 at 11:37
@Giorgio: "Java 7 or 8 oracle made an exception in the compiler to recognize str1 == str2 as str1.equals(str2)" Nope, the Java Language Specification says: Reference Equality Operators: "If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality." That's all folks. I didn't find anything new about this in the Java 8 early draft either. – David Tonhofer Jan 21 '14 at 23:47

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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