49

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?

16
  • 12
    You might want to look at Scala, where == is object equality and eq is reference equality (ofps.oreilly.com/titles/9780596155957/…).
    – Giorgio
    Commented Apr 2, 2013 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
    Commented Apr 2, 2013 at 16:07
  • 10
    @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
    Commented Apr 2, 2013 at 19:29
  • 1
    @aviv the == operator only maps to Equals if the == operator was implemented that way. The default behavior for == is the same as ReferenceEquals (actually, ReferenceEquals is defined as the object version of ==) Commented Apr 3, 2013 at 11:49
  • 3
    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? Commented Oct 20, 2015 at 15:23

9 Answers 9

92

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.

10
  • 8
    Special cases add astonishment.
    – Blrfl
    Commented Apr 2, 2013 at 21:13
  • 17
    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
    Commented Apr 3, 2013 at 0:27
  • 15
    @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. Commented Apr 3, 2013 at 6:14
  • 3
    @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
    Commented Apr 3, 2013 at 18:45
  • 1
    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.) Commented Apr 3, 2013 at 19:10
31

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.

14
  • 50
    Ah, yes, the old "lets' blunt the pointy tool so the oafs won't hurt themselves" excuse.
    – Blrfl
    Commented Apr 2, 2013 at 11:38
  • 22
    @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
    Commented Apr 2, 2013 at 11:43
  • 15
    -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. Commented Apr 2, 2013 at 13:04
  • 12
    @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
    Commented Apr 2, 2013 at 14:58
  • 9
    @Blrfl: I don't mind the oafs hurting themselves. It's when they accidentially poke my eye out that I get annoyed.
    – Jonas
    Commented Apr 2, 2013 at 21:21
9

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 reference types, == compares reference equality
    • 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 addition of auto boxing/unboxing of primitive wrappers (e.g. java.lang.Integer, etc.) muddles things a bit because comparing an int with an Integer (or vice-versa) with == will indeed compare the integer values of each of those things, but if both variables are of type Integer then using == may give an unexpected answer where the integer values are equal but the references are different, yielding a false comparison.

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.

9
  • 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
    Commented Oct 19, 2015 at 20:52
  • 3
    @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).
    – user40980
    Commented Oct 19, 2015 at 20:52
  • 1
    As to language design, CS.SE advertises that it may be on topic there.
    – user40980
    Commented Oct 19, 2015 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
    Commented Oct 19, 2015 at 21:03
  • 2
    "Consistency within the language" also helps with generics
    – Brendan
    Commented Oct 20, 2015 at 14:22
2

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.

15
  • 1
    "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
    Commented Apr 2, 2013 at 11:03
  • @Giorgio: Exactly. See my comment on Gilad Naaman's answer.
    – Blrfl
    Commented Apr 2, 2013 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. Commented Apr 2, 2013 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
    Commented Apr 2, 2013 at 11:16
  • 1
    @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. Commented Apr 2, 2013 at 11:29
2

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.

3
  • how does this answer the question asked?
    – gnat
    Commented Apr 3, 2013 at 12:53
  • See last sentence (which I separated in a proper paragraph in a edit). Commented Apr 3, 2013 at 18:23
  • 1
    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
    Commented Feb 25, 2014 at 13:27
1

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)

11
  • "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
    Commented Apr 2, 2013 at 10:55
  • 2
    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
    Commented Apr 2, 2013 at 11:02
  • 1
    @WillihamTotland: Consider the opposite case. Currently, if I create two strings, s1 and s2 and give them the same contents, they pass the equality (s1.equals(s2)) comparison but not the same-reference (==) comparison because they're two different objects. Changing the semantics of == to mean equality would cause s1 == s2 to evaluate true where it used to evaluate false.
    – Blrfl
    Commented Apr 2, 2013 at 11:30
  • 2
    @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. Commented Apr 2, 2013 at 11:37
  • 2
    @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. Commented Jan 21, 2014 at 23:47
0

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.

9
  • 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
    Commented Nov 17, 2014 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
    Commented Nov 17, 2014 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
    Commented Nov 17, 2014 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
    Commented Nov 18, 2014 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
    Commented Nov 18, 2014 at 4:54
0

In general, there is very good reason to want to be able to test if two object references point to the same object. I've had plenty of times that I've written

Address oldAddress;
Address newAddress;
... populate values ...
if (oldAddress==newAddress)
... etc ...

I may or may not have an equals function in such cases. If I do, the equals function may compare the entire contents of both objects. Often it just compares some identifier. "A and B are references to the same object" and "A and B are two different objects with the same content" are, of course, two very different ideas.

It's probably true that for immutable objects, like Strings, this is less of an issue. With immutable objects, we tend to think of the object and the value as being the same thing. Well, when I say "we", I mean "I", at least.

Integer three=new Integer(3);
Integer triangle=new Integer(3);
if (three==triangle) ...

Of course that returns false, but I can see someone thinking it should be true.

But once you say that == compares reference handles and not contents for Objects in general, making a special case for Strings would be potentially confusing. As someone else on here said, what if you wanted to compare the handles of two String objects? Would there be some special function to do it only for Strings?

And what about ...

Object x=new String("foo");
Object y=new String("foo");
if (x==y) ...

Is that false because they are two different objects, or true because they are Strings whose contents are equal?

So yes, I understand how programmers get confused by this. I've done it myself, I mean write if myString == "foo" when I meant if myString.equals("foo"). But short of redesigning the meaning of the == operator for all objects, I don't see how to address it.

6
  • Note that other modern languages on the JVM, such as Scala, use == to mean "equal strings".
    – Andres F.
    Commented Mar 14, 2016 at 15:16
  • @AndresF. (shrug) In Java, "<" means "less than", while in XML it "opens a tag". In VB "=" can mean "is equal to", while in Java it's only used for assignment. Etc. It's hardly surprising that different languages use different symbols to mean the same thing, or the same symbol to mean different things.
    – Jay
    Commented Mar 14, 2016 at 18:31
  • It wasn't a dig at your answer. It was just a comment. I was just pointing out that a more modern language than Java took the chance to redesign the meaning of ==, just as you mentioned at the end.
    – Andres F.
    Commented Mar 14, 2016 at 18:33
  • @AndresF. And my reply wasn't a dig at your comment, just saying that different languages approach these issues in different ways. :-) I actually like the way VB handles this ... pause for the hisses and boos from the VB haters ... "=" always compares the value (for system-defined types), whether primitive or object. "Is" compares the handles of two objects. That seems more intuitive to me.
    – Jay
    Commented Mar 14, 2016 at 18:59
  • Sure. But Scala is way closer to Java than Visual Basic. I like to think Scala's designers realized that Java's use of == was error prone.
    – Andres F.
    Commented Mar 14, 2016 at 19:08
0

This is a valid question for Strings, and not only for strings, but also for other immutable Objects representing some "value", e.g. Double, BigInteger, and even InetAddress.

For making the == operator usable with Strings and other value-classes, I see three alternatives:

  • Have the compiler know about all these value-classes and the way to compare their contents. If it were just a handful of classes from the java.lang package, I'd consider that, but that doesn't cover cases like InetAddress.

  • Allow operator overloading so a class defines its == comparison behaviour.

  • Remove the public constructors and have static methods returning instances from a pool, always returning the same instance for the same value. To avoid memory leaks, you need something like SoftReferences in the pool, which didn't exist in Java 1.0. And now, to maintain compatibility, the String() constructors can't be removed anymore.

The only thing that could still be done today would be to introduce operator overloading, and personally I don't wouldn't like Java to go that route.

To me, code readability is most important, and a Java programmer knows that the operators have a fixed meaning, defined in the language specification, whereas methods are defined by some code, and their meaning has to be looked up in the method's Javadoc. I'd like to stay with that distinction even if it means that String comparisons will not be able use the == operator.

There's just one aspect of Java's comparisons that's annoying to me: the effect of auto-boxing and -unboxing. It hides the distinction between the primitive and the wrapper type. But when you compare them with ==, they are VERY different.

    int i=123456;
    Integer j=123456;
    Integer k=123456;
    System.out.println(i==j);  // true or false? Do you know without reading the specs?
    System.out.println(j==k);  // true or false? Do you know without reading the specs?

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