Scenario: You place a collection of objects in a JCF collection for later use. You are working with a closed group of objects and wish to infer object equality by means of object identity.
Problem: The objects may implement equals and hashCode specifically and the JCF classes make heavy use of these internally. You wish to avoid having the methods called.
Example:
Set<Item> items = new HashSet<Item>(); items.add(item1); items.add(item2); ... if (items.contains(item1)) { ... }
Solution 1: Wrap the items in objects that implement equals through object identity (the identity of the wrapped object).
class ItemWrapper { private final Item item; public ItemWrapper(Item item) { this.item = item; } public Item getItem() { return item; } public boolean equals(Object o) { return o == item; } public int hashCode() { return System.identityHashCode(item); } } ... Set<ItemWrapper> items = new HashSet<ItemWrapper>(); set.add(item1Wrapper); set.add(item2Wrapper); ... if (items.contains(new ItemWrapper(item1))) { ... }
Solution 2: Proxy the item objects. This solution works well if you’ve embraced coding to interfaces (i.e., if Item is an interface), which makes proxying straightforward. (When you proxy by extending instead of implementing, you run duplicate initialization that may cause side effects.)
Be warned, however, that processing that must override the equals method should typically not leak such proxies to their clients.
public class ItemProxy implements Item { private Item item; public ItemProxy(Item item) { this.item = item; } public String getName() { return item.getName(); } public void setName(String name) { item.setName(name); } ... public boolean equals(Object o) { return o == item; } public int hashCode() { return System.identityHashCode(item); } } ... Set<Item> items = new HashSet<Item>(); set.add(item1Proxy); set.add(item2Proxy); ... if (items.contains(new ItemProxy(item1))) { ... } ... return items;
Any client code using ItemProxy instances that escape your custom processing must be aware of the altered semantics of equals and hashCode. Remember that equals and hashCode semantics may already be specified as part of the general contract of the Item interface, informally, through JavaDocs.
interface Item { /** * Tests whether this item equals the given object. * This method MUST implement business key equality * for proper use within persistence managers. * * @param obj the object to compare against this item * * @return a boolean indicating whether this item * equals the given object */ boolean equals(Object obj); }
In this case, if you inadvertently leak an ItemProxy into the world, you’re fucked.
Post a Comment
You must be logged in to post a comment.