Scala: Ignore case class field for equals/hascode? - class

Is it possible to ignore a field of a case class in the equals/haschode method of the case class?
My use case is that I have a field that is essentially metadata for rest of the data in the class.

Only parameters in the first parameter section are considered for equality and hashing.
scala> case class Foo(a: Int)(b: Int)
defined class Foo
scala> Foo(0)(0) == Foo(0)(1)
res0: Boolean = true
scala> Seq(0, 1).map(Foo(0)(_).hashCode)
res1: Seq[Int] = List(-1669410282, -1669410282)
UPDATE
To expose b as a field:
scala> case class Foo(a: Int)(val b: Int)
defined class Foo
scala> Foo(0)(1).b
res3: Int = 1

scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Foo private(x: Int, y: Int) {
def fieldToIgnore: Int = 0
}
object Foo {
def apply(x: Int, y: Int, f: Int): Foo = new Foo(x, y) {
override lazy val fieldToIgnore: Int = f
}
}
// Exiting paste mode, now interpreting.
defined class Foo
defined module Foo
scala> val f1 = Foo(2, 3, 11)
f1: Foo = Foo(2,3)
scala> val f2 = Foo(2, 3, 5)
f2: Foo = Foo(2,3)
scala> f1 == f2
res45: Boolean = true
scala> f1.## == f2.##
res46: Boolean = true
You may override .toString if necessary.

You can override the equals and hasCode methods in a case class
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Person( val name:String, val addr:String) {
override def equals( arg:Any) = arg match {
case Person(s, _) => s == name
case _ => false
}
override def hashCode() = name.hashCode
}
// Exiting paste mode, now interpreting.
scala> Person("Andy", "") == Person("Andy", "XXX")
res2: Boolean = true
scala> Person("Andy", "") == Person("Bob", "XXX")
res3: Boolean = false

If you override toString in the base class it will not be overridden by the derived case classes. Here is an example:
sealed abstract class C {
val x: Int
override def equals(other: Any) = true
}
case class X(override val x: Int) extends C
case class Y(override val x: Int, y: Int) extends C
Than we you test:
scala> X(3) == X(4)
res2: Boolean = true
scala> X(3) == X(3)
res3: Boolean = true
scala> X(3) == Y(2,5)
res4: Boolean = true

Related

Case class hashCode and equals override optional attributes

How to override hashcode and equals method if it's all attributes are optional, I tried below, what's the better way to write hashCode and equals
case class A(id: Option[String], name: Option[String]){
override def hashCode(): Int = ???
override def canEqual(a: Any) = a.isInstanceOf[A]
override def equals(obj: Any): Boolean = obj match {
case obj: A => {
obj.canEqual(this) && this.id == obj.id && this.name == obj.name
}
case _ => false
}
}
You can do without the luxury equals if you're not boxing primitives or nulls.
That can save some null checks and instanceof/checkcast.
scala> case class C(id: Option[String], name: Option[String]) {
| override def equals(other: Any) = other match { case c: C => cmp(id, c.id) && cmp(name, c.name) case _ => false }
| private def cmp(x: Option[String], y: Option[String]): Boolean =
| if (x eq None) y eq None else !(y eq None) && x.get.equals(y.get)
| }
defined class C
scala> val x = C(Option("king"), Option("kong"))
x: C = C(Some(king),Some(kong))
scala> val y = C(Option("king"), Option("kong"))
y: C = C(Some(king),Some(kong))
scala> x == y
res0: Boolean = true
scala> val y = C(Option("king"), Option("king"))
y: C = C(Some(king),Some(king))
scala> x == y
res1: Boolean = false
But:
scala> val y = C(Option("king"), Some(null))
y: C = C(Some(king),Some(null))
scala> x == y
res2: Boolean = false
scala> y == x
java.lang.NullPointerException
at C.cmp(<console>:4)
at C.equals(<console>:2)
... 28 elided
scala> val y = C(Option("king"), null)
y: C = C(Some(king),null)
scala> x == y
java.lang.NullPointerException
at C.cmp(<console>:4)
at C.equals(<console>:2)
... 28 elided
There's not much to gain from avoiding hashCode of None, which is just "None".##.
Illustrating loss of some comparison:
scala> :pa
// Entering paste mode (ctrl-D to finish)
case class C(id: Option[String], name: Option[Any]) {
override def equals(other: Any) = other match { case c: C => cmp(id, c.id) && cmp(name, c.name) case _ => false }
private def cmp[A](x: Option[A], y: Option[A]) = if (x eq None) y eq None else !(y eq None) && x.get.equals(y.get)
}
// Exiting paste mode, now interpreting.
defined class C
scala> val x = C(Option("king"), Option('k'))
x: C = C(Some(king),Some(k))
scala> val y = C(Option("king"), Option('k'.toInt))
y: C = C(Some(king),Some(107))
scala> x == y
res19: Boolean = false
scala> case class K(id: Option[String], name: Option[Any])
defined class K
scala> K(Option("king"), Option('k')) == K(Option("king"), Option('k'.toInt))
res20: Boolean = true

Is the Symbol a case object?

{ sealed trait Sealed; case object Foo extends Sealed; case class Bar(s: String) extends Sealed; trait Baz extends Sealed }
import scala.reflect.runtime.universe._
val List(bar, baz, foo) = symbolOf[Sealed].asClass.knownDirectSubclasses.toList
I've tried .asClass.primaryConstructor.isStatic, but that doesn't work if the sealed trait is defined as dependent type.
Symbol#isModuleClass looks like it can determine if the symbol is an object, and foo.asClass.isClass can determine whether it has the case modifier. Note that asClass will throw an exception if you use it on other type of symbol, though (method, term, etc).
Object test:
scala> bar.isModuleClass // case class
res28: Boolean = false
scala> baz.isModuleClass // trait
res29: Boolean = false
scala> foo.isModuleClass // case object
res30: Boolean = true
For other types:
scala> val a = ""
a: String = ""
scala> symbolOf[a.type].isModuleClass
res34: Boolean = false
case class A(value: String) ; object A { def default = A("") }
scala> symbolOf[A].isModuleClass
res35: Boolean = false
scala> symbolOf[A.type].isModuleClass
res36: Boolean = true
Based on the API documentation and SI-6012 it seems like isModule should also work, but it only returns true for the companionSymbol.
Case test:
scala> bar.asClass.isCaseClass // case class
res44: Boolean = true
scala> baz.asClass.isCaseClass // trait
res45: Boolean = false
scala> foo.asClass.isCaseClass // case object
res46: Boolean = true
For other types (same definitions as the examples above):
scala> symbolOf[a.type].asClass.isCaseClass // plain singleton object
res47: Boolean = false
scala> symbolOf[A].asClass.isCaseClass // case class
res48: Boolean = true
scala> symbolOf[A.type].asClass.isCaseClass // non-case object
res49: Boolean = false
Putting it together:
def isCaseObject(symbol: Symbol): Boolean =
symbol.isModuleClass && symbol.asClass.isCaseClass
scala> isCaseObject(bar)
res50: Boolean = false
scala> isCaseObject(baz)
res51: Boolean = false
scala> isCaseObject(foo)
res52: Boolean = true
scala> isCaseObject(symbolOf[a.type])
res53: Boolean = false
scala> isCaseObject(symbolOf[A])
res54: Boolean = false
scala> isCaseObject(symbolOf[A.type])
res55: Boolean = false

Infer a return type of a method based on the type expected in call site?

So, basically, what I want to do is:
object WithoutWrap {
def f[T: ClassTag](x: String): T = {
println("Class of T is really… " ++ implicitly[ClassTag[T]].toString)
??? : T
}
def y: Int = f("abc")
def z: Int = f[Int]("abc")
}
In both cases I’d like the inferred T to be Int. Let’s run this:
scala> WithoutWrap.y
Class of T is really… Nothing
scala.NotImplementedError: an implementation is missing
scala> WithoutWrap.z
Class of T is really… Int
scala.NotImplementedError: an implementation is missing
Unfortunately it’s Nothing in the first case.
However, if we return the T wrapped in something,
object WithWrap {
trait Wrap[T]
def f[T: ClassTag](x: String): Wrap[T] = {
println("Class of T is really… " ++ implicitly[ClassTag[T]].toString)
??? : Wrap[T]
}
def y: Wrap[Int] = f("abc")
def z: Wrap[Int] = f[Int]("abc")
}
… the T is inferred correctly in both cases:
scala> WithWrap.y
Class of T is really… Int
scala.NotImplementedError: an implementation is missing
scala> WithWrap.z
Class of T is really… Int
scala.NotImplementedError: an implementation is missing
How to get Int in both cases without wrapping?
Depending on what you're trying to accomplish, overload resolution is sensitive to expected type:
scala> case class A(s: String) ; case class B(s: String)
defined class A
defined class B
scala> :pa
// Entering paste mode (ctrl-D to finish)
object X {
def f(s: String): A = A(s)
def f(s: String)(implicit d: DummyImplicit): B = B(s)
}
// Exiting paste mode, now interpreting.
defined object X
scala> val x: A = X f "hi"
x: A = A(hi)
scala> val y: B = X f "hi"
y: B = B(hi)

How to filter methods by return type with scala reflection?

I'm trying to test whether a method's return type exactly matches a supplied type.
Somehow, I've found two String Types that are not equal.
class VarAndValue {
#BeanProperty
val value = "";
}
class ScalaReflectionTest {
#Test
def myTest(): Unit = {
val type1 = universe.typeOf[String]
// Get return value of VarAndValue.getValue
val type2 = universe.typeOf[VarAndValue].
declarations.
filter { m => m.name.decoded == "getValue" && m.isMethod }.
head.
asInstanceOf[universe.MethodSymbol].
returnType
println(type1) // String
println(type2) // String
println(type1.getClass())
println(type2.getClass()) // !=type1.getClass() !!
println(type1==type2) // False
}
}
yields...
String
String
class scala.reflect.internal.Types$TypeRef$$anon$3
class scala.reflect.internal.Types$TypeRef$$anon$6
false
How can I filter methods of a class by their return type? (It seems very difficult if I can test for equality of the return types).
Scala 2.10
UPDATE:
I can't drop into the Java reflection universe because this erased type information for generics like List[Int] (which become List[Object] is java-land where they're really List[java.lang.Integer]). I need my matched to pay attention to the generic parameter information that the scala type universe preserves.
Use =:= to compare types.
scala> import beans._
import beans._
scala> :pa
// Entering paste mode (ctrl-D to finish)
class VarAndValue {
#BeanProperty
val value = "";
}
// Exiting paste mode, now interpreting.
defined class VarAndValue
scala> import reflect.runtime._, universe._
import reflect.runtime._
import universe._
scala> val str = typeOf[String]
str: reflect.runtime.universe.Type = String
scala> val ms = typeOf[VarAndValue].declarations filter (m => m.isMethod && m.name.decoded == "getValue")
warning: there were two deprecation warnings; re-run with -deprecation for details
ms: Iterable[reflect.runtime.universe.Symbol] = SynchronizedOps(method getValue)
scala> val mm = ms.toList
mm: List[reflect.runtime.universe.Symbol] = List(method getValue)
scala> val m = mm.head.asInstanceOf[MethodSymbol]
m: reflect.runtime.universe.MethodSymbol = method getValue
scala> val t = m.returnType
t: reflect.runtime.universe.Type = java.lang.String
scala> println(t)
java.lang.String
scala> println(str)
String
scala> str == t
res2: Boolean = false
scala> str match { case `t` => }
scala.MatchError: String (of class scala.reflect.internal.Types$AliasNoArgsTypeRef)
... 33 elided
scala> str =:= t
res4: Boolean = true
scala> str match { case TypeRef(a,b,c) => (a,b,c) }
res5: (reflect.runtime.universe.Type, reflect.runtime.universe.Symbol, List[reflect.runtime.universe.Type]) = (scala.Predef.type,type String,List())
scala> t match { case TypeRef(a,b,c) => (a,b,c) }
res6: (reflect.runtime.universe.Type, reflect.runtime.universe.Symbol, List[reflect.runtime.universe.Type]) = (java.lang.type,class String,List())
Depending on what you're going for, consider that conforming types can have differing representations:
scala> typeOf[List[Int]]
res11: reflect.runtime.universe.Type = scala.List[Int]
scala> type X = List[Int]
defined type alias X
scala> typeOf[X].dealias
res12: reflect.runtime.universe.Type = List[scala.Int]
scala> res11 =:= res12
res13: Boolean = true
scala> res11 == res12
res14: Boolean = false
scala> typeOf[X] <:< res11
res15: Boolean = true
scala> typeOf[X] =:= res11
res16: Boolean = true
Turns out this is the way to compare types ...
type1 match {
case type2 => true
case _ => false
}
which gives a different answer than type1==type.
Scala is a strange beast.

What's the neatest way to define circular lists with Scala?

Here is my attempt:
case class A(val a: A, val b: Int){
override def toString() = b.toString
}
lazy val x: A = A(y, 0)
lazy val y: A = A(z, 1)
lazy val z: A = A(x, 2)
The problem comes when trying to do anything with x; causing x to be evaluated starts off a circular evaluation going through x, y, z and ends in a stack overflow. Is there a way of specifying that val a should be computed lazily?
You could use Stream like this:
lazy val stream: Stream[Int] = 0 #:: 1 #:: 2 #:: stream
stream.take(10).toList
// List(0, 1, 2, 0, 1, 2, 0, 1, 2, 0)
In general you should use call-by-name parameters:
class A(_a: => A, val b: Int) {
lazy val a = _a
override def toString() = s"A($b)"
}
Usage:
scala> :paste
// Entering paste mode (ctrl-D to finish)
lazy val x: A = new A(y, 0)
lazy val y: A = new A(z, 1)
lazy val z: A = new A(x, 2)
// Exiting paste mode, now interpreting.
x: A = <lazy>
y: A = <lazy>
z: A = <lazy>
scala> z.a.a.a.a.a
res0: A = A(1)
You need to make A.a itself lazy.
You can do it by turning it into a by name parameter that is used to initialize a lazy field:
class A(a0: => A, val b: Int){
lazy val a = a0
override def toString() = b.toString
}
object A {
def apply( a0: => A, b: Int ) = new A( a0, b )
}
You could also do the same using a helper class Lazy:
implicit class Lazy[T]( getValue: => T ) extends Proxy {
def apply(): T = value
lazy val value = getValue
def self = value
}
It has the advantage that you code is pretty much unchanged except for changing a: A into a: Lazy[A]:
case class A(val a: Lazy[A], val b: Int){
override def toString() = b.toString
}
Note that to access the actual value wrapped in Lazy, you can either use apply or value (as in x.a() or x.a.value)
You can define a lazy circular list using the Stream data type:
lazy val circular: Stream[Int] = 1 #:: 2 #:: 3 #:: circular
You can do the same trick on your own with by-name parameters:
class A(head: Int, tail: => A)
lazy val x = new A(0, y)
lazy val y = new A(1, z)
lazy val z = new A(2, x)
Note that this does not work with case classes.
You could use a by-name parameter.
class A(__a: => A, val b: Int) {
def a = __a
override def toString() = b.toString
}
object A {
def apply(a: => A, b: Int) = new A(a, b)
}