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
Related
I have the code that instance.get returns value, and based on the type I process accordingly.
instance.get match {
case v:Range => {
val sizeInBytes = util.conversion.Util.getBytesForBits(v.size)
val value = v.decode(contentByteArray.slice(index, index + sizeInBytes))
index += sizeInBytes
res(key) = value
}
case v:Encoding => {
val sizeInBytes = util.conversion.Util.getBytesForBits(v.size)
val value = v.decode(contentByteArray.slice(index, index + sizeInBytes))
index += sizeInBytes
res(key) = value
}
...
}
In the code, I have duplication for the Range and Encoding type. How can I merge the two cases?
I tried the | operator, but it doesn't work.
case v:Range | v:Encoding
This can't work, because Range.size and Encoding.size are two completely different methods despite the fact that they are named the same. And same is true for Range.decode and Edncoding.decode.
So, when you write v.size, the type of v has to be known, it has to be either v:Encoding or v:Range, not v:Encoding|v:Range.
How to fix this? Make a common trait like this:
trait SomethingWithDecodeAndSize {
def size: Int
def decode(bytes: Array[Byte]): Whatever
}
And then, change the definitions of Range and Encoding:
class Range extends SomethingWithDecodeAndSize { ... }
class Encoding extends SomethingWithDecodeAndSize { ... }
Now you can just do case v: SomethingWithDecodeAndSize => ... in your match clause.
Also ... Don't do instance.get, that's bad taste. Do instead
instance match {
Some(v: SomethingWithDecodeAndSize) => ...
}
Update
If you cannot modify the definitions of the original classes, you can use an extractor:
object SomethingWithDecodeAndSize {
def unapply(a: Any): Option[SomethingWithDecodeAndSize] = a match {
case r: Range => Some(new SomethingWithDecodeAndSize {
def size = r.size
def decode(bytes: Array[Byte]) = r.decode(bytes)
})
case r: Encoding => Some(new SomethingWithDecodeAndSize {
def size = r.size
def decode(bytes: Array[Byte]) = r.decode(bytes)
})
case _ => None
}
}
Now, you can do case Some(SomethingWithDecodeAndSize(v)) => ... in your match.
An alternate solution to #Dima's in case you can't change definition of Range and Encoding (and there is no supertype with required methods):
trait RangeOrEncoding {
def size: Int
def decode(bytes: Array[Byte]): Whatever
}
implicit def liftRange(r: Range): RangeOrEncoding = new RangeOrEncoding {
def size = r.size
def decode(bytes: Array[Byte]) = r.decode(bytes)
}
// similar conversion for Encoding
// can also be a local def
private def handleRangeOrEncoding(v: RangeOrEncoding) = {
val sizeInBytes = util.conversion.Util.getBytesForBits(v.size)
val value = v.decode(contentByteArray.slice(index, index + sizeInBytes))
index += sizeInBytes
res(key) = value
}
instance match {
case Some(v: Range) => handleRangeOrEncoding(v)
case Some(v: Encoding) => handleRangeOrEncoding(v)
...
}
I remember the cheerleaders in high school asking us, "How loose is your goose?"
scala> class C { def f(i: Int) = 2 * i }
defined class C
scala> class D { def f(i: Int) = 3 * i }
defined class D
scala> def test(x: Any) = x match { case y: { def f(i: Int): Int } => y.f(42) }
<console>:11: warning: a pattern match on a refinement type is unchecked
def test(x: Any) = x match { case y: { def f(i: Int): Int } => y.f(42) }
^
warning: there was one feature warning; re-run with -feature for details
test: (x: Any)Int
scala> test(new C)
res0: Int = 84
scala> test(new D)
res1: Int = 126
scala> test(42)
java.lang.NoSuchMethodException: java.lang.Integer.f(int)
at java.lang.Class.getMethod(Class.java:1786)
at .reflMethod$Method1(<console>:11)
at .test(<console>:11)
... 32 elided
I believe the answer was: "Loose, baby, loose."
Edit:
scala> import reflect.runtime._,universe._,language.reflectiveCalls
import reflect.runtime._
import universe._
import language.reflectiveCalls
scala> class C { def f(i: Int) = 2 * i }
defined class C
scala> class D { def f(i: Int) = 3 * i }
defined class D
scala> def f[A](a: A)(implicit tt: TypeTag[A]) = a match {
| case b: { def f(i: Int): Int }
| if tt.tpe <:< typeOf[{ def f(i: Int): Int }] =>
| b.f(42)
| }
<console>:19: warning: a pattern match on a refinement type is unchecked
case b: { def f(i: Int): Int }
^
f: [A](a: A)(implicit tt: reflect.runtime.universe.TypeTag[A])Int
scala> f(new C)
res0: Int = 84
scala> f(new D)
res1: Int = 126
scala> f(3) // now an ordinary MatchError
scala.MatchError: 3 (of class java.lang.Integer)
at .f(<console>:18)
... 32 elided
So you can express it as an ordinary type bounds:
scala> def f[A <: { def f(i: Int): Int }](a: A) = a.f(42)
f: [A <: AnyRef{def f(i: Int): Int}](a: A)Int
scala> f(new C)
res3: Int = 84
scala> f(17)
<console>:20: error: inferred type arguments [Int] do not conform to method f's type parameter bounds [A <: AnyRef{def f(i: Int): Int}]
f(17)
^
<console>:20: error: type mismatch;
found : Int(17)
required: A
f(17)
^
You still need to accept the cost of the reflective call, of course.
Are these two partial functions equivalent?
val f0: PartialFunction[Int, String] = {
case 10 => "ten"
case n: Int => s"$n"
}
val f1 = new PartialFunction[Int, String] {
override def isDefinedAt(x: Int): Boolean = true
override def apply(v: Int): String = if (v == 10) "ten" else s"$v"
}
UPD
val pf = new PartialFunction[Int, String] {
def isDefinedAt(x: Int) = x == 10
def apply(v: Int) = if (isDefinedAt(v)) "ten" else "undefined"
}
def fun(n: Int)(pf: PartialFunction[Int, String]) = pf.apply(n)
println(fun(100)(pf))
Is it truly PF now?
I think you need 2 partial (value) functions to use the PartialFunction the way it is designed to be: one for the value 10, and the other for the other Ints:
val f0:PartialFunction[Int, String] = { case 10 => "ten" }
val fDef:PartialFunction[Int, String] = { case n => s"$n" }
And how to apply them:
val t1 = (9 to 11) collect f0
t1 shouldBe(Array("ten"))
val t2 = (9 to 11) map (f0 orElse fDef)
t2 shouldBe(Array("9", "ten", "11"))
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)
}
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
Having seen the answers coming out of questions like this one involving horror shows like trying to catch the NPE and dredge the mangled name out of the stack trace, I am asking this question so I can answer it.
Comments or further improvements welcome.
Like so:
case class ?:[T](x: T) {
def apply(): T = x
def apply[U >: Null](f: T => U): ?:[U] =
if (x == null) ?:[U](null)
else ?:[U](f(x))
}
And in action:
scala> val x = ?:("hel")(_ + "lo ")(_ * 2)(_ + "world")()
x: java.lang.String = hello hello world
scala> val x = ?:("hel")(_ + "lo ")(_ => (null: String))(_ + "world")()
x: java.lang.String = null
Added orElse
case class ?:[T](x: T) {
def apply(): T = x
def apply[U >: Null](f: T => U): ?:[U] =
if (x == null) ?:[U](null)
else ?:[U](f(x))
def orElse(y: T): T =
if (x == null) y
else x
}
scala> val x = ?:(obj)(_.subField)(_.subSubField).orElse("not found")
x: java.lang.String = not found
Or if you prefer named syntax as opposed to operator syntax
case class CoalesceNull[T](x: T) {
def apply(): T = x
def apply[U >: Null](f: T => U): CoalesceNull[U] =
if (x == null) CoalesceNull[U](null)
else CoalesceNull[U](f(x))
def orElse(y: T): T =
if (x == null) y
else x
}
scala> val x = CoalesceNull(obj)(_.subField)(_.subSubField).orElse("not found")
x: java.lang.String = not found
More examples
case class Obj[T](field: T)
test("last null") {
val obj: Obj[Obj[Obj[Obj[String]]]] = Obj(Obj(Obj(Obj(null))))
val res0 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field)()
res0 should === (null)
val res1 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field).orElse("not found")
res1 should === ("not found")
}
test("first null") {
val obj: Obj[Obj[Obj[Obj[String]]]] = null
val res0 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field)()
res0 should === (null)
val res1 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field).orElse("not found")
res1 should === ("not found")
}
test("middle null") {
val obj: Obj[Obj[Obj[Obj[String]]]] = Obj(Obj(null))
val res0 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field)()
res0 should === (null)
val res1 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field).orElse("not found")
res1 should === ("not found")
}
test("not null") {
val obj: Obj[Obj[Obj[Obj[String]]]] = Obj(Obj(Obj(Obj("something"))))
val res0 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field)()
res0 should === ("something")
val res1 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field).orElse("not found")
res1 should === ("something")
}
}