Skip to content

Instantly share code, notes, and snippets.

@benjaminjackman
Created December 2, 2009 10:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benjaminjackman/247104 to your computer and use it in GitHub Desktop.
Save benjaminjackman/247104 to your computer and use it in GitHub Desktop.
import java.lang.ref.SoftReference
object CSoftRef {
def apply[A](blk : => A) = new CSoftRef[A](blk)
}
class CSoftRef[A](blk : => A) extends MagicMixin {
private var ref = new SoftReference[A](blk)
def get : A = {
ref.get match {
case null => {
val x = blk
ref = new SoftReference[A](blk)
x
}
case x => x
}
}
override def eqFields = List(get)
override def canEqual(that : Any) = that.isInstanceOf[CSoftRef[_]]
override def toString() = get.toString
}
/**
* Mix this into any class and it will give you a to string automatically
* as well as perform equals and hashcode
*/
object MagicMixin {
def hash(f: Any) = f match {
case null => 0
case f => f.hashCode
}
def hashCode(fields: List[Any]): Int = {
fields.foldLeft(0)((x, f) => x + hash(f) * 31)
}
final def equals(a: List[Any], b: List[Any]): Boolean = {
if (a.isEmpty || b.isEmpty) {
true
} else if (a.head == b.head) {
equals(a.tail, b.tail)
} else {
false
}
}
}
/**
* DO NOT MIX THE MAGIC MIXIN WITH CASE CLASSES. Case classes do there own magic and this will just
* screw them up!
*
* Automatically provides equals, hashCode, and toString. Be careful when overriding
* hashCode and equals in the subclass.
*
* <code>
* trait Point extends MagicMixin {
* override def eqFields = List(x,y)
* override def name = "Point"
* override def canEqual(that : Any) = that.isInstanceOf[Point]
* def x : Int
* def y : Int
* }
* </code>
*
*
*
*/
trait MagicMixin {
/**
* is this class able to be equal to that class?
* usually just use:
* override def canEqual(that : Any) = that.isInstanceOf[YOUR_CLASS_NAME]
*
*/
def canEqual(that: Any): Boolean
/**
* The fields to check for equivalence
*
*/
def eqFields: List[Any]
/**
* The name to use for the to String
*
*/
def showName: String = getClass.getSimpleName
/**
* The fields to to display in the toString. Do not need
* to override unless you want to show more or less fields that what you are
* doing your equals on.
*
*/
def showFields = eqFields
override def toString(): String = showFields.mkString(showName + "(", ",", ")")
override def equals(that: Any): Boolean = {
if (this eq that.asInstanceOf[AnyRef]) {
true
} else {
that match {
case that: MagicMixin => (that canEqual this) && (this canEqual that) && MagicMixin.equals(this.eqFields, that.eqFields)
case _ => false
}
}
}
override def hashCode(): Int = MagicMixin.hashCode(eqFields)
}
/**
* Mixin into classes with a lot fields that are immutable to have the hash code set properly.
* WARNING ONLY MIXIN WITH IMMUTABLE CLASSES!
*
*/
trait CachedMagicMixin extends MagicMixin {
val hash = CSoftRef[Int] {
super.hashCode()
}
val string = CSoftRef[String] {
super.toString()
}
override def hashCode() = {
hash.get
}
override def toString() = {
string.get
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment