From f85a6749de6502d4138b4f6013286088c330d74f Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 9 Jun 2009 15:11:27 +0000 Subject: A couple findbugs inspired bugfixes, and a new ... A couple findbugs inspired bugfixes, and a new trait scala.util.Hashable, now used by GenericRange. --- src/library/scala/Range.scala | 10 ++++++- src/library/scala/util/Hashable.scala | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/library/scala/util/Hashable.scala (limited to 'src/library') diff --git a/src/library/scala/Range.scala b/src/library/scala/Range.scala index 3c8138fb87..d7ee1d7a38 100644 --- a/src/library/scala/Range.scala +++ b/src/library/scala/Range.scala @@ -13,6 +13,7 @@ package scala import collection.immutable.Vector import collection.generic.VectorView import util.control.Exception.catching +import util.Hashable /**

* GenericRange is a generified version of the @@ -36,7 +37,7 @@ import util.control.Exception.catching abstract class GenericRange[T] (val start: T, val end: T, val step: T, val isInclusive: Boolean = false) (implicit num: Integral[T]) -extends VectorView[T, Vector[T]] with RangeToString[T] { +extends VectorView[T, Vector[T]] with RangeToString[T] with Hashable { import num._ // todo? - we could lift the length restriction by implementing a range as a sequence of @@ -109,6 +110,13 @@ extends VectorView[T, Vector[T]] with RangeToString[T] { catching(classOf[ClassCastException]) opt doContains getOrElse super.contains(_x) } + + // Using trueEnd gives us Range(1, 10, 1).inclusive == Range(1, 11, 1) + val hashValues = List(start, trueEnd, step) + override def equals(other: Any) = other match { + case x: GenericRange[_] => this equalHashValues x + case _ => false + } } private[scala] trait RangeToString[T] extends VectorView[T, Vector[T]] { diff --git a/src/library/scala/util/Hashable.scala b/src/library/scala/util/Hashable.scala new file mode 100644 index 0000000000..88d3b72fbd --- /dev/null +++ b/src/library/scala/util/Hashable.scala @@ -0,0 +1,55 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.util + +/** A convenience trait for simplifying hashCode creation. + * Mix this into a class and define val hashValues = List(x1, x2, ...) + * and your hashCode will be derived from those values. If you define + * equals in terms of equalHashValues then your hashCode and equals + * methods will never be out of sync. Something like: + * + * override def equals(other: Any) = other match { + * case x: YourClass => this equalHashValues x + * case _ => false + * } + * + * @author Paul Phillips + */ +abstract trait Hashable extends AnyRef +{ + import Hashable._ + protected def hashValues: List[Any] // in an ideal universe this would be more like List[Hashable] + protected def hashSeed: Int = 0 + + override def hashCode: Int = + (hashValues map calculateHashCode).foldLeft(hashSeed)((x, y) => x * 41 + y) + + protected def equalHashValues(other: Any) = other match { + case x: Hashable => hashValues == x.hashValues + case _ => false + } +} +abstract trait StrictHashable extends Hashable +{ + protected def hashValues: List[Hashable] +} + +object Hashable +{ + /** This implicit is for StrictHashable's benefit, so your hashValues list + * can contain both explicitly Hashable classes and value types. + */ + implicit def anyVal2Hashable(x: AnyVal): Hashable = + new Hashable { protected def hashValues = List(x) } + + private def calculateHashCode(x: Any) = x match { + case null => 0 + case x: AnyRef => x.hashCode + case x => x.asInstanceOf[AnyRef].hashCode + } +} \ No newline at end of file -- cgit v1.2.3