I've been pondering for a while why Java and C# (and I'm sure other languages) default to reference equality for ==.

In the programming I do (which certainly is only a small subset of programming problems), I almost always want logical equality when comparing objects instead of reference equality. I was trying to think of why both of these languages went this route instead of inverting it and having == be logical equality and using .ReferenceEquals() for reference equality.

Obviously using reference equality is very simple to implement and it gives very consistent behavior, but it doesn't seem like it fits well with most of the programming practices I see today.

I don't wish to seem ignorant of the issues with trying to implement a logical comparison, and that it has to be implemented in every class. I also realize that these languages were designed a long time ago, but the general question stands.

Is there some major benefit of defaulting to this that I am simply missing, or does it seem reasonable that the default behavior should be logical equality, and defaulting back to reference equality it a logical equality doesn't exist for the class?

  • 3
    Because variables are references? Since variables act like pointers it makes sense for them to be compared like it – Daniel Gratzer Jun 9 '13 at 5:51
  • C# uses logical equality for value types like structs. But what should the "default logical equality" be for two objects of different reference types? Or for two objects where one is of a type A inherited from B? Always "false" like for structs? Even when you have the same object referenced twice, first as A, then as B? Does not make much sense to me. – Doc Brown Jun 9 '13 at 6:25
  • 3
    In other words, are you asking why in C#, if you override Equals(), it doesn't automatically change the behavior of ==? – svick Jun 9 '13 at 11:11

C# does it because Java did. Java did because Java does not support operator overloading. Since value equality must be redefined for each class, it could not be an operator, but instead had to be a method. IMO this was a poor decision. It is much easier to both write and read a == b than a.equals(b), and much more natural for programmers with C or C++ experience, but a == b is almost always wrong. Bugs from the use of == where .equals was required have wasted countless thousands of programmer hours.

  • 7
    I think there are as many supporters of operator overloading as there are detractors, so I would not say "it was a poor decision" as an absolute statement. Example: in the C++ project I work in we have overloaded the == for many classes and a few months ago I discovered a few developers did not know what == was actually doing. There is always this risk when the semantics of some construct is not obvious. The equals() notation tells me that I am using a custom method and that I have to look it up somewhere. Bottom line: I think operator overloading is an open issue in general. – Giorgio Jun 9 '13 at 8:36
  • 9
    I'd say Java has no user-defined operator overloading. Plenty of operators have double (overloaded) meanings in Java. Look at + for example, which does addition (of numeric values) and concatenation of strings at the same time. – Joachim Sauer Jun 9 '13 at 8:39
  • 14
    How can a == b be more natural for programmers with C experience, since C doesn't support user-defined operator overloading? (For example, the C way to compare strings is strcmp(a, b) == 0, not a == b.) – svick Jun 9 '13 at 11:12
  • This is basically what I thought, but I figured I'd ask those with more experience to make sure I wasn't missing something obvious. – Zipper Jun 10 '13 at 3:30
  • 4
    @svick: in C, there is no string type, nor any reference types. String operations are done via char *. It seems obvious to me that comparing two pointers for equality is not the same as a string comparison. – kevin cline Jun 10 '13 at 20:01

The short answer: Consistency

To answer your question properly, though, I suggest we take a step backwards and look to the issue of what equality means in a programming language. There are at least THREE different possibilities, which are used in various languages:

  • Reference equality: means that a = b is true if a and b refer to the same object. It would not be true if a and b referred to different objects, even if all the attributes of a and b were the same.
  • Shallow equality: means that a = b is true if all the attributes of the objects to which a and b refer are identical. Shallow equality can easily be implemented by a bitwise comparison of the memory space that represents the two objects. Please note that reference equality implies shallow equality
  • Deep equality: means that a = b is true if each attribute in a and b is either identical or deeply equal. Please note that deep equality is implied by both reference equality and shallow equality. In this sense, deep equality is the weakest form of equality and reference equality is the strongest.

These three types of equality are often used because they are convenient to implement: all three equality checks can easily be generated by a compiler (in the case of deep equality, the compiler might need to use tag bits to prevent infinite loops if a structure to be compared has circular references). But there is another problem: none of these might be appropriate.

In non-trivial systems, equality of objects is often defined as something between deep and reference equality. To check whether we want to regard two objects as equal in a certain context, we might require some attributes to be compared by where it stands in memory and others by deep equality, while some attributes may be allowed to be something different altogether. What we would really like is a “forth type of equality”, a really nice one, often called in the literature semantic equality. Things are equal if they are equal, in our domain. =)

So we can come back to your question:

Is there some major benefit of defaulting to this that I am simply missing, or does it seem reasonable that the default behavior should be logical equality, and defaulting back to reference equality if a logical equality doesn't exist for the class?

What do we mean when we write ‘a == b’ in any language? Ideally, it should always be the same: Semantic equality. But that´s not possible.

One of the main considerations is that, at least for simple types like numbers, we expect that two variables are equal after assignment of the same value. See below:

var a = 1;
var b = a;
if (a == b){
a = 3;
b = 3;
if (a == b) {

In this case, we expect that ‘a equals b’ in both statements. Anything else would be insane. Most (if not all) of the languages follow this convention. Therefore, with simple types (aka values) we know how to achieve semantic equality. With objects, that can be something completely different. See below:

var a = new Something(1);
var b = a;
if (a == b){
b = new Something(1);
if (a == b) {

We expect that the first ‘if’ will always be true. But what do you expect on the second ‘if’? It really depends. Can ‘DoSomething’ change the (semantic) equality of a and b?

The problem with semantic equality is that it cannot be automatically generated by the compiler for objects, nor it´s obvious from the assignments. A mechanism must be provided for the user to define semantic equality. In object-oriented languages, that mechanism is an inherited method: equals. Reading a piece of OO code, we don´t expect a method to have the same exact implementation in all classes. We are used to inheritance and overloading.

With operators, though, we expect the same behavior. When you see ‘a == b’ you should expect the same type of equality (from the 4 above) in all situations. So, aiming for consistency the languages designers used reference equality for all types. It should not depend on whether a programmer has overridden a method or not.

PS: The language Dee is slightly different from Java and C#: the equals operator means shallow equality for simple types and semantic equality for user-defined classes (with the responsibility for implementing the = operation lying with the user — no default is provided). As, for simple types, shallow equality is always semantic equality, the language is consistent. The price it pays, though, is that the equals operator is by default undefined for user-defined types. You have to implement it. And, sometimes, that´s just boring.

  • 2
    When you see ‘a == b’ you should expect the same type of equality (from the 4 above) in all situations. The language designers of Java used reference equality for objects and semantic equality for primitives. It's not obvious to me that this was the right decision, or that this decision is more "consistent" than allowing == to be overloaded for semantic equality of objects. – Charles Salvia Jun 12 '13 at 20:14
  • They used "the equivalent of reference equality" for primitives too. When you use "int i = 3" there are no pointers for the number, so you can´t use references. With strings, a "sort of" primitive type, it´s more evident: you have to use the ".intern()" or a direct assignment (String s = "abc") to use == (reference equality). – Hbas Jun 12 '13 at 20:20
  • 1
    PS: C#, on the other hand, was not consistent with it´s strings. And IMHO, in this case, that´s much better. – Hbas Jun 12 '13 at 20:28
  • @CharlesSalvia: In Java, if a and b are the same type, the expression a==b tests whether a and b hold the same thing. If one of them holds a reference to object #291, and the other holds a reference to object #572, they don't hold the same thing. The contents of object #291 and #572 may be equivalent, but the variables themselves hold different things. – supercat Jan 17 '14 at 18:40
  • 2
    @CharlesSalvia It is designed in such a way that you can see a == b and know what it does. Likewise, you can see a.equals(b) and presume that a overloads equals. If a == b calls a.equals(b) (if implemented), is it comparing by reference or by content? Don't remember? You have to check class A. The code is no longer as quick to read if you're not even sure what is being called. It'd be as if methods with the same signature were allowed, and the method being called depends on what the current scope is. Such programs would be impossible to read. – Neil May 8 '15 at 7:56

I was trying to think of why both of these languages went this route instead of inverting it and having == be logical equality and using .ReferenceEquals() for reference equality.

Because the latter approach would be confusing. Consider:

if (null.ReferenceEquals(null)) System.out.println("ok");

Should this code print "ok", or should it throw a NullPointerException?

  • ReferenceEquals is static, taking the two references to compare as parameters. – Joe Sewell Apr 13 '20 at 3:49

For Java and C# the benefit lies in their being object oriented.

From a performance point of view - the easier to write code should also be quicker: since OOP intends for logically distinct elements to be represented by different objects, checking reference equality would be quicker, taking into consideration that objects can become quite large.

From a logical point of view - equality of an object to another does not have to be as obvious as comparing to object's properties for equality (ex. how is null==null logically interpreted? this can differ from case to case).

I think what it boils down to, is your observation that "you always want logical equality over reference equality". The consensus amongst the language designers was probably the opposite. I personally find it hard to evaluate this, since I lack the broad spectrum of programming experience. Roughly, I use reference equality more in optimisation algorithms, and logical equality more in handling data-sets.

  • 7
    Reference equality has nothing to do with object-orientation. Quite the opposite, actually: one of the fundamental properties of object-orientation is that objects that have the same behavior are indistinguishable. One object must be able to simulate another object. (After all, OO was invented for simulation!) Reference equality allows you to distinguish between two different objects that have the same behavior, it allows you to distinguish between a simulated object and a real one. Therefore, Reference Equality breaks object-orientation. An OO program must not use Reference Equality. – Jörg W Mittag Jun 9 '13 at 10:55
  • @JörgWMittag: To do object-oriented program properly requires that there be a means of asking object X whether its state is equal to that of Y [a potentially transient condition], and also a means of asking object X whether it is equivalent to Y [X is equivalent to Y only if its state is guaranteed to be eternally equal to Y]. Having separate virtual methods for equivalence and state-equality would be good, but for many types, reference inequality will imply non-equivalence, and there's no reason to spend time on virtual method dispatch to prove it. – supercat Jan 17 '14 at 18:52

.equals() compares variables by their contents. instead of == that compares the objects by their contents...

using objects is more accurate tu use .equals()

  • 3
    You assumption is incorrect. .equals() does whatever .equals() was coded to do. It is normally by contents, but it doesn't have to be. Also it's not more accurate to use .equals(). It just depends on what you are trying to accomplish. – Zipper Jun 12 '13 at 22:02

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