From 669bba35bcada8d764f74af2b1b6ccd145c1346d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 1 Nov 2022 07:28:15 -0700 Subject: [PATCH] Restore IterableWrapper equals and hashCode --- .../convert/JavaCollectionWrappers.scala | 10 ++- .../scala/collection/convert/EqualsTest.scala | 78 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 test/junit/scala/collection/convert/EqualsTest.scala diff --git a/src/library/scala/collection/convert/JavaCollectionWrappers.scala b/src/library/scala/collection/convert/JavaCollectionWrappers.scala index 49d6596b1a44..29c3dcbac5db 100644 --- a/src/library/scala/collection/convert/JavaCollectionWrappers.scala +++ b/src/library/scala/collection/convert/JavaCollectionWrappers.scala @@ -56,7 +56,15 @@ private[collection] object JavaCollectionWrappers extends Serializable { } @SerialVersionUID(3L) - class IterableWrapper[A](val underlying: Iterable[A]) extends ju.AbstractCollection[A] with IterableWrapperTrait[A] with Serializable + class IterableWrapper[A](val underlying: Iterable[A]) extends ju.AbstractCollection[A] with IterableWrapperTrait[A] with Serializable { + import scala.runtime.Statics._ + override def equals(other: Any): Boolean = + other match { + case other: IterableWrapper[_] => underlying.equals(other.underlying) + case _ => false + } + override def hashCode = finalizeHash(mix(mix(0xcafebabe, "IterableWrapper".hashCode), anyHash(underlying)), 1) + } @SerialVersionUID(3L) class JIterableWrapper[A](val underlying: jl.Iterable[A]) diff --git a/test/junit/scala/collection/convert/EqualsTest.scala b/test/junit/scala/collection/convert/EqualsTest.scala new file mode 100644 index 000000000000..b3f9ae17176b --- /dev/null +++ b/test/junit/scala/collection/convert/EqualsTest.scala @@ -0,0 +1,78 @@ + +package scala.collection.convert + +import org.junit.Test +import org.junit.Assert._ + +import scala.jdk.CollectionConverters._ +import JavaCollectionWrappers._ + +import java.util.{AbstractList, AbstractSet, List => JList, Set => JSet} + +class JTestList(vs: Int*) extends AbstractList[Int] { + def this() = this(Nil: _*) + override def size = vs.size + override def get(i: Int) = vs(i) +} +class JTestSet(vs: Int*) extends AbstractSet[Int] { + def this() = this(Nil: _*) + require(vs.toSet.size == vs.size) + override def size = vs.size + override def iterator = vs.iterator.asJava +} + +/** Test that collection wrappers forward equals and hashCode where appropriate. */ +class EqualsTest { + + def jlstOf(vs: Int*): JList[Int] = new JTestList(vs: _*) + def jsetOf(vs: Int*): JSet[Int] = new JTestSet(vs: _*) + + // Seq extending AbstractList inherits equals + + @Test def `List as JList has equals`: Unit = { + val list = List(1, 2, 3) + val jlst = new SeqWrapper(list) + assertEquals(jlstOf(1, 2, 3), jlst) + assertEquals(jlst, jlstOf(1, 2, 3)) + assertTrue(jlst == jlstOf(1, 2, 3)) + assertEquals(jlst.hashCode, jlst.hashCode) + } + + @Test def `Set as JSet has equals`: Unit = { + val set = Set(1, 2, 3) + val jset = new SetWrapper(set) + assertEquals(jsetOf(1, 2, 3), jset) + assertEquals(jset, jsetOf(1, 2, 3)) + assertTrue(jset == jsetOf(1, 2, 3)) + assertEquals(jset.hashCode, jset.hashCode) + } + + @Test def `Map as JMap has equals`: Unit = { + val map = Map(1 -> "one", 2 -> "two", 3 -> "three") + val jmap = new MapWrapper(map) + assertEquals(jmap, jmap) + } + + @Test def `Anything as Collection is equal to Anything`: Unit = { + def set = Set(1, 2, 3) + def jset = new IterableWrapper(set) + assertTrue(jset == jset) + assertEquals(jset, jset) + assertNotEquals(jset, set) + assertEquals(jset.hashCode, jset.hashCode) + } + + @Test def `Iterator wrapper does not compare equal`: Unit = { + def it = List(1, 2, 3).iterator + def jit = new IteratorWrapper(it) + assertNotEquals(jit, jit) + assertNotEquals(jit.hashCode, jit.hashCode) + } + + @Test def `Anything.asScala Iterable has case equals`: Unit = { + def vs = jlstOf(42, 27, 37) + def it = new JListWrapper(vs) + assertEquals(it, it) + assertEquals(it.hashCode, it.hashCode) + } +}