-4

I was thinking about mutable objects and how they're weird (but very cool).

Question: can a mutable object not equal itself?

Only caveat here is that obviously you must override equals method, otherwise the default checks pointer equality which will (obviously) always be satisfied.

Edit to Question

Alright, I've thoroughly confused everyone, take a look at this program:

import java.util.Random;

public class EqualsTest {
    private static final Random RANDOM = new Random(System.currentTimeMillis());

    private int value = 0;

    public static void main(String... args) {
        System.out.println("Starting program...");
        final EqualsTest test = new EqualsTest();
        final Thread modify = new Thread(new Runnable() {
            public void run() {
                while (true)
                    test.value = RANDOM.nextInt(Integer.MAX_VALUE);
            }
        });
        final Thread equals = new Thread(new Runnable() {
            public void run() {
                while (true)
                    if (!test.equals(test)) {
                        System.out.println("test didn't equal test");
                    }
            }
        });

        modify.start();
        equals.start();
    }

    @Override
    public boolean equals(Object e) {
        if (!(e instanceof EqualsTest))
            return false;
        final EqualsTest obj = (EqualsTest) e;
        return this.value == obj.value;
    }
}
Zoe
  • 27,060
  • 21
  • 118
  • 148
Jared
  • 940
  • 5
  • 9
  • 3
    If you override `equals` then obviously it will return whatever you tell it to return. Being mutable or not is irrelevant. I'm not sure what your aim is with this question. – khelwood Dec 04 '21 at 22:47
  • @khelwood You have indeed missed the point...how is it possible for a "sane" equals method to return false on itself? – Jared Dec 05 '21 at 00:32
  • @Jared your `equals(...)`-implementation IS faulty. It can lead to a situation where `x.equals(x)` returns `false`. This is exactly the case I described in my answer. – Turing85 Dec 05 '21 at 07:22
  • @Turing85 by "faulty" you mean it's not thread safe--which was my point. – Jared Dec 05 '21 at 08:14
  • @Jared No, by faulty I mean it violates the contract. As I have shown in my answer, this can be achieved without concurrency. The violation is in the question itself: "*can a mutable object not equal itself?*" - yes, but this inherently breaks the contract of `equals(...)`. – Turing85 Dec 05 '21 at 08:37
  • @Turing85 I think we're talking "at" each other now (probably mostly my fault, sorry). I think (think) you're saying that we _cannot_ have an object for which $o.equals(o) = false$ because it violates the definition of equals() method and therefore won't behave as an object (like we expect). My question is "who cares about a method contract", are we allowed to do this? Then answer is yes (you trivially gave an example, imo) but even if we are "sane", we can still easily violate this contract with mutable objects. – Jared Dec 05 '21 at 08:47
  • 1
    @Jared "*My question is "who cares about a method contract", are we allowed to do this? Then answer is yes (you trivially gave an example, imo) but even if we are "sane", we can still easily violate this contract with mutable objects.*" - Agreed 100%. The contract of `equals(...)` as documented cannot be enforced by the runtime, thus it is documented. And yes: incorrect `equals(...)`-implementations are easy to not notice (until the program begins acting weirdly). – Turing85 Dec 05 '21 at 08:50
  • 1
    Answers do not belong in questions! Do not edit the answer into the question. You can update the question with new information, but do not add the answer into the question. Please take the [tour]. – Cody Gray - on strike Dec 05 '21 at 13:54
  • 1
    You cannot delete a question that has been answered. Comments are regularly deleted, especially those that are obsolete or provide no useful information. There was no reason for me to keep comments that were based purely on a misunderstanding. – Cody Gray - on strike Dec 05 '21 at 14:04

1 Answers1

4

The documentation of Object::equals clearly states that:

...

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.

...

While we can implement equals(...) in a way that violates this contract (as was mentioned by khelwood in this comment), it will have consequences. Collection CopyOnWriteArraySet, for example, will not function properly:

import java.util.concurrent.CopyOnWriteArraySet;

class Ideone {
  public static void main(String[] args) {
    final CopyOnWriteArraySet<Foo> foos = new CopyOnWriteArraySet<>();
    final Foo foo = new Foo();
    foos.add(foo);
    System.out.println(foos.size()); // prints "1"
    foos.add(foo);
    System.out.println(foos.size()); // prints "2"
  }
}

class Foo {
  @Override
  public boolean equals(Object o) {
    return false;
  }
}

Ideone demo

(This is a variant of the code I used in my answer to this question by betaRide)


The collection CopyOnWriteArraySet will behave as expected when we remove the (faulty) implementation of equals(...) in Foo and use the default equals(...)-implementation from Object instead:

class Foo {
}

Ideone demo

Turing85
  • 18,217
  • 7
  • 33
  • 58
  • You're missing my point about idempotent operations. – Jared Dec 05 '21 at 00:39
  • 1
    Your question was: "*can a mutable object not equal itself?*". And I answered this question. Besides: your post does not contain the phrase "idem". – Turing85 Dec 05 '21 at 07:15
  • "It is reflexive: for any non-null reference value x, x.equals(x) should return true"...where is this enforced? If it's true then the equals method should come with synchronization built into the proverbial cake. For obvious (performance) reasons we probably don't want that but if using mutable objects and equality is a required, it's something that needs to be handled elsewhere (i.e. ensure equals is accessed in a thread safe way). – Jared Dec 05 '21 at 08:27
  • It is enforced right there: in the contract of the method, written down in the documentation. – Turing85 Dec 05 '21 at 08:34
  • That response confounds me, "it's enforced because you're required to enforce it." This is why things don't work--that mentality. – Jared Dec 05 '21 at 08:38
  • You presented a trivially incorrect equals method, then showed how it breaks things. I showed (at least what I think is) a completely reasonable equals method that also doesn't work (in a threaded program)...showing that the equals method is not thread safe (which I'm guessing is no big surprise to anyone based on the feedback). – Jared Dec 05 '21 at 08:40
  • 1
    Again: `equals(...)` has a contract. The contract cannot (easily) be enforced on a technical level, thus the contract is documented. My trivial implementation of `equals(...)` and your implementation of `equals(...)` break the contract at the same point: the reflexive property. Both implementations of `equals(...)` miss a common safeguard (`if (this == o) {return true; }`) - which would defeat the point of the question. Every implementation of `equals(...)` that allows `x.equals(x)` to return `false` will result in the problem I described above. – Turing85 Dec 05 '21 at 08:43