Created
December 2, 2009 10:13
-
-
Save benjaminjackman/247104 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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