[COLLECTIONS-543] AbstractCollectionDecorator does not forward equals and hashCode anymore to the backing collection.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1651115 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Thomas Neidhart 2015-01-12 15:21:18 +00:00
parent 32155107dd
commit 30e5023265
15 changed files with 146 additions and 12 deletions

View File

@ -22,6 +22,9 @@
<body>
<release version="4.1" date="TBD" description="">
<action issue="COLLECTIONS-543" dev="tn" type="fix">
"AbstractCollectionDecorator" doesn't forward equals and hashCode anymore.
</action>
<action issue="COLLECTIONS-544" dev="tn" type="fix" due-to="Oswaldo Olivo">
Documented runtime complexity of "CollectionUtils#retainAll(Collection, Collection).
</action>

View File

@ -63,6 +63,16 @@ public abstract class AbstractBagDecorator<E>
return (Bag<E>) super.decorated();
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
//-----------------------------------------------------------------------
public int getCount(final Object object) {

View File

@ -87,6 +87,16 @@ public class PredicatedBag<E> extends PredicatedCollection<E> implements Bag<E>
return (Bag<E>) super.decorated();
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
//-----------------------------------------------------------------------
public boolean add(final E object, final int count) {

View File

@ -82,6 +82,23 @@ public class SynchronizedBag<E> extends SynchronizedCollection<E> implements Bag
return (Bag<E>) decorated();
}
@Override
public boolean equals(final Object object) {
if (object == this) {
return true;
}
synchronized (lock) {
return getBag().equals(object);
}
}
@Override
public int hashCode() {
synchronized (lock) {
return getBag().hashCode();
}
}
//-----------------------------------------------------------------------
public boolean add(final E object, final int count) {

View File

@ -18,10 +18,10 @@ package org.apache.commons.collections4.bag;
import java.util.Set;
import org.apache.commons.collections4.set.TransformedSet;
import org.apache.commons.collections4.Bag;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.collection.TransformedCollection;
import org.apache.commons.collections4.set.TransformedSet;
/**
* Decorates another {@link Bag} to transform objects that are added.
@ -110,6 +110,16 @@ public class TransformedBag<E> extends TransformedCollection<E> implements Bag<E
return (Bag<E>) decorated();
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
//-----------------------------------------------------------------------
public int getCount(final Object object) {

View File

@ -342,6 +342,16 @@ public abstract class AbstractDualBidiMap<K, V> implements BidiMap<K, V> {
this.parent = parent;
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
@Override
public boolean removeAll(final Collection<?> coll) {
if (parent.isEmpty() || coll.isEmpty()) {

View File

@ -34,6 +34,19 @@ import java.util.Iterator;
* {@link #iterator()}. Instead it simply returns the value from the
* wrapped collection. This may be undesirable, for example if you are trying
* to write an unmodifiable implementation it might provide a loophole.
* <p>
* This implementation does not forward the hashCode and equals methods through
* to the backing object, but relies on Object's implementation. This is necessary
* to preserve the symmetry of equals. Custom definitions of equality are usually
* based on an interface, such as Set or List, so that the implementation of equals
* can cast the object being tested for equality to the custom interface.
* AbstractCollectionDecorator does not implement such custom interfaces directly;
* they are implemented only in subclasses. Therefore, forwarding equals would break
* symmetry, as the forwarding object might consider itself equal to the object being
* tested, but the reverse could not be true. This behavior is consistent with the
* JDK's collection wrappers, such as {@link java.util.Collections#unmodifiableCollection(Collection)}.
* Use an interface-specific subclass of AbstractCollectionDecorator, such as
* AbstractListDecorator, to preserve equality behavior, or override equals directly.
*
* @param <E> the type of the elements in the collection
* @since 3.0
@ -144,16 +157,6 @@ public abstract class AbstractCollectionDecorator<E>
return decorated().retainAll(coll);
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
@Override
public String toString() {
return decorated().toString();

View File

@ -65,6 +65,16 @@ public abstract class AbstractListDecorator<E> extends AbstractCollectionDecorat
return (List<E>) super.decorated();
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
//-----------------------------------------------------------------------
public void add(final int index, final E object) {

View File

@ -94,6 +94,16 @@ public class PredicatedList<E> extends PredicatedCollection<E> implements List<E
return (List<E>) super.decorated();
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
//-----------------------------------------------------------------------
public E get(final int index) {

View File

@ -114,6 +114,16 @@ public class TransformedList<E> extends TransformedCollection<E> implements List
return (List<E>) decorated();
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
//-----------------------------------------------------------------------
public E get(final int index) {

View File

@ -24,6 +24,12 @@ import org.apache.commons.collections4.collection.AbstractCollectionDecorator;
* Decorates another {@link Queue} to provide additional behaviour.
* <p>
* Methods are forwarded directly to the decorated queue.
* <p>
* This implementation does not forward the hashCode and equals methods through
* to the backing object, but relies on Object's implementation. This is
* necessary as some Queue implementations, e.g. LinkedList, have custom a
* equals implementation for which symmetry can not be preserved.
* See class javadoc of AbstractCollectionDecorator for more information.
*
* @param <E> the type of the elements in the queue
* @since 4.0

View File

@ -63,4 +63,14 @@ public abstract class AbstractSetDecorator<E> extends AbstractCollectionDecorato
return (Set<E>) super.decorated();
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
}

View File

@ -86,4 +86,14 @@ public class PredicatedSet<E> extends PredicatedCollection<E> implements Set<E>
return (Set<E>) super.decorated();
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
}

View File

@ -101,4 +101,14 @@ public class TransformedSet<E> extends TransformedCollection<E> implements Set<E
super(set, transformer);
}
@Override
public boolean equals(final Object object) {
return object == this || decorated().equals(object);
}
@Override
public int hashCode() {
return decorated().hashCode();
}
}

View File

@ -29,6 +29,7 @@ import java.util.Set;
import org.apache.commons.collections4.AbstractObjectTest;
import org.apache.commons.collections4.Bag;
import org.apache.commons.collections4.BulkTest;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapIterator;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.bag.AbstractBagTest;
@ -754,7 +755,7 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
(MultiValuedMap<?, ?>) readExternalFormFromDisk(getCanonicalFullCollectionName(map));
assertEquals("Map is the right size", map.size(), map2.size());
for (final Object key : map.keySet()) {
assertEquals("Map had inequal elements", map.get(key), map2.get(key));
assertTrue("Map had inequal elements", CollectionUtils.isEqualCollection(map.get(key), map2.get(key)));
if (isRemoveSupported()) {
map2.remove(key);
}
@ -1071,10 +1072,12 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
return AbstractMultiValuedMapTest.this.makeObject().asMap();
}
@Override
public Map<K, Collection<V>> makeFullMap() {
return AbstractMultiValuedMapTest.this.makeFullMap().asMap();
}
@Override
@SuppressWarnings("unchecked")
public K[] getSampleKeys() {
K[] samplekeys = AbstractMultiValuedMapTest.this.getSampleKeys();
@ -1085,6 +1088,7 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
return (K[]) finalKeys;
}
@Override
@SuppressWarnings("unchecked")
public Collection<V>[] getSampleValues() {
V[] sampleValues = AbstractMultiValuedMapTest.this.getSampleValues();
@ -1095,6 +1099,7 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
return colArr;
}
@Override
@SuppressWarnings("unchecked")
public Collection<V>[] getNewSampleValues() {
Object[] sampleValues = { "ein", "ek", "zwei", "duey", "drei", "teen" };