Question

Let's say I have a domain object, Here is the pseudocode

public class SampleClass {
  id
  title
  ... a lot of other fields from database
  public boolean equals(AnotherObj) {
       return this.id.equals(AnotherObj.id);
  }
}

The equals method is based on the id, which works for most scenarios. But in one scenario, the two objects shall be considered equal if the titles are the same. The objects are put in list and list.contains(obj) and list.indexOf(obj) is heavily invoked for business logic, which depends on the equals method.

How to override the equals method at runtime? The sampleClass object could contain complicated tree childrens and I don't want to copy it in the memory.

I want to create a wrapper for the existing object. The wrapper only contains the specific equals method, all other method call should still be passed to the existing object.

I looked into cglib but it has to generate the target object by itself, it cannot wrap an existing object.

Any easy way?

Was it helpful?

Solution

Assuming you are actually talking about Object's .equals()/.hashCode(), which is not what your code currently does, you can do that using Guava's Equivalence.

Create an equivalence:

public final class SampleClassIdEquivalence
    extends Equivalence<SampleClass>
{
    // override .doEquivalent(), .doHash()
}

Then you can use it like so:

final Equivalence<SampleClass> byId = new SampleClassIdEquivalence();

byId.equivalent(o1, o2);

Set<Equivalence.Wrapper<SampleClass>> set = ...

set.add(byId.wrap(whatever));

Then you can create other equivalences for equality by name or whatever; your choice.

In fact, Equivalence is pretty much to .equals()/.hashCode() what Comparator is to Comparable.

OTHER TIPS

Rather than trying to wrap these objects, I would modify the class so that it understands what it should be checking (in each scenario) in its equals method. Then in this particular situation, just set whatever state member (or whatever it is) that tells the object how equals should work. Just be sure not to break the contract.

I would also recommend actually overriding Object#equals (e.g., make sure your equals accepts Object, not just AnotherObj). (And of course, as always, do hashCode as well.)

But if you do want to do this, and you want to do it with equals, it sounds like a job for the Facade pattern It's a bit of a pain in Java: You have to create another whole class that accepts the object as an argument in the constructor, implements all methods except equals by just calling them on the object, and then (of course) implements equals differently.

So basically:

class SampleSubclass extends SampleClass {
    SampleClass inst;

    SampleSubclass(SampleClass inst) {
        this.inst = inst;
    }

    @override
    String someMethod() {
        return inst.someMethod();
    }

    // ...and so on...

    @override
    public boolean equals(AnotherObj other) { // <== But this really should accept Object, not AnotherObj
        // ... implementation...
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top