Case class equals with list of byte array - scala

I am trying to use the should matchers on a case class
case class ListOfByteArrayCaseConfig(
#BeanProperty
permissions: java.util.List[Array[Byte]]
)
With the following test case
val orig = ListOfByteArrayCaseConfig(List(Array[Byte](10, 20, 30)))
val orig2 = ListOfByteArrayCaseConfig(List(Array[Byte](10, 20, 30)))
orig2 should be === orig
Obviously this would fail because the two byte arrays are not equal reference wise. What I want to do is somehow make this work without changing the test case code and still keeping the case class.
Is it even possible? (like adding a custom equals method to the case class?)

I found the solution. Apparently I can override the equals method in a case class
Scala: Ignore case class field for equals/hascode?
Though it gets rid of the reason for using case classes in the first place which is to simplify data objects.
case class ListOfByteArrayCaseConfig(
#BeanProperty
permissions: java.util.List[Array[Byte]]
) {
override def equals(arg: Any): Boolean = {
val obj = arg.asInstanceOf[ListOfByteArrayCaseConfig]
var i: Int = 0
for (i <- 0 until permissions.size()) {
if (!util.Arrays.equals(permissions.get(i), obj.permissions.get(i))) {
return false
}
}
return true
}
}

Related

Regex pattern equality

In ScalaTest, I have the following check:
"abc".r shouldBe "abc".r
But it is not equal. I don't understand.
abc was not equal to abc
ScalaTestFailureLocation: com.ing.cybrct.flink.clickstream.ConfigsTest$$anonfun$6 at (ConfigsTest.scala:97)
Expected :abc
Actual :abc
While it's possible to decide whether two regular expressions accept the same language, it seems to be rather complicated and not all that terribly useful for everyday regex usage. Therefore, equality on compiled regex patterns is just referential equality:
val x = "abc".r
val y = "abc".r
x == y
// res0: Boolean = false
The method shouldBe in Scalatest 3.0.5 delegates the equality check to the areEqualComparingArraysStructurally method:
def shouldBe(right: Any): Assertion = {
if (!areEqualComparingArraysStructurally(leftSideValue, right)) {
val (leftee, rightee) = Suite.getObjectsForFailureMessage(leftSideValue, right)
val localPrettifier = prettifier // Grabbing a local copy so we don't attempt to serialize AnyShouldWrapper (since first param to indicateFailure is a by-name)
indicateFailure(FailureMessages.wasNotEqualTo(localPrettifier, leftee, rightee), None, pos)
}
else indicateSuccess(FailureMessages.wasEqualTo(prettifier, leftSideValue, right))
}
which in turn simply delegates the equality check (as you can expect) to the == operator:
private[scalatest] def areEqualComparingArraysStructurally(left: Any, right: Any): Boolean = {
// Prior to 2.0 this only called .deep if both sides were arrays. Loosened it
// when nearing 2.0.M6 to call .deep if either left or right side is an array.
// TODO: this is the same algo as in scalactic.DefaultEquality. Put that one in
// a singleton and use it in both places.
left match {
case leftArray: Array[_] =>
right match {
case rightArray: Array[_] => leftArray.deep == rightArray.deep
case _ => leftArray.deep == right
}
case _ => {
right match {
case rightArray: Array[_] => left == rightArray.deep
case _ => left == right
}
}
}
}
In Scala, at least on the JVM, == simply calls equals, which, if not overridden, checks whether the compared variables point to the same object. case classes are peculiar in that the compiler overrides equals for you to compare the constructor arguments.
You can test it very easily with the following (but as you can imagine, the same applies to simply using == on your own):
package org.example
import org.scalatest.{FlatSpec, Matchers}
final class MyClass(val a: Int)
final case class MyCaseClass(a: Int)
final class TestSpec extends FlatSpec with Matchers {
"equality on case classes" should "succeed" in {
new MyCaseClass(1) shouldBe new MyCaseClass(1)
}
"equality on non-case classes" should "fail" in {
new MyClass(1) shouldNot be(new MyClass(1))
}
"equality between Regex objects" should "fail" in {
"abc".r shouldNot be("abc".r)
}
}
What the r method does is instantiating a new Regex object, which is not a case class and does not override the equality definition, thus yielding the result you see.
Yes, they are not equal. Both "abc".r are different references to two different memory locations. shouldbe is to check equality but not identity.

Why Scala case class copy method parameterised only with the variables defined in the case class?

Why Scala case class copy method parameterised only with the variables defined in the case class?
The question based on on the Q&A:
Case class copy does not maintain state of an inherited trait
Short summary - when trait is defined with a field and extended by a case class, copy on the case class will create the new class instance only with the variables defined in case class, without the extended trait.
trait A {
var list: List[Int] = List()
def add(element: Int) = {
list = element :: list
}
}
case class B(str: String) extends A {
val b = B("foo")
println("B1: " + b.list)
b.add(1)
b.add(2)
b.add(3)
println("B2: " + b.list)
val b2 = b.copy(str = "bar")
println("B3: " + b.list)
println("B4: " + b2.list)
}
Here, B4: () will be empty, while B3:(3,2,1)
Because there is no reasonable way to do what you want consistently.
For your example, generated code for copy could be
def copy(str: String = this.str, list: List[Int] = this.list): B = {
val newB = B(str)
newB.list = list
newB
}
Good enough. Now what happens if you change list to be private, or a val instead of a var? In both cases newB.list = ... won't compile, so what code should the compiler generate?
If you really want to keep the value of list after copying (considering the mutability issue I mentioned in the comments of OP), you can write your own copy method.
case class B(str: String) extends A {
def copy(str: String = this.str, list: List[Int] = this.list): B = {
val newB = B(str)
list.reverse.foreach(newB.add)
newB
}
}

Maintaining a Map of case class objects in Scala

I want to maintain a Map of case class objects, such that I can add new instances and look them up by an ID.
My current (very ugly) solution (stripped down):
case class cc(i: Int)
var myccmap: Map[Int, cc] = null
def addcc(thecc: cc): cc = {
if (myccmap == null) {
myccmap = Map(thecc.hashCode, thecc)
}
else {
myccmap = myccmap ++ Map(thecc.hashCode, thecc)
}
thecc
}
And then elsewhere I can use val somecc = addcc(cc(56)), for example, to maintain a Map of my cc objects added with addcc.
This way, I can store the key, which is just the hashCode, with some data in a file, and then when I read the file, I can extract the cc object by looking up the hashCode (which may or may not be present in myccmap).
Is there a better way to do this, ideally without relying on a check for null?
Your code can be simplified, just use a HashSet if you want to use the hash anyway:
import collection.immutable.HashSet
case class Cc(i: Int)
var myccmap: HashSet[Cc] = HashSet.empty
def addcc(thecc: Cc): thecc.type = {
myccmap += thecc
thecc
}
Also, by convention, classes should start with an uppercase letter. I also used a singleton-type as return value of addcc, this way it's clear that this function really just returns its parameter
Depends on whether you really want that key or not. I suppose the key of hashcode is still required because you store the key somewhere. And be noticed, don't use var in most of the cases, instead, use mutable map will help.
case class cc(i: Int)
val myccmap: mutable.Map[Int, cc] = mutable.Map.empty
def addcc(thecc: cc): cc = {
myccmap += (thecc.hashCode -> thecc)
thecc
}

Will the var members in case class affect case class's equality?

I have made heavy use of case classes in my code, replying on the underlying equality definitions of case class to behave correctly. Then now I found that I need to add another field member to a case class.
So if I add a var field member in case class, will it mess up the equality attributes for the case class?
If 1 is yes, then what if I just change the var field value once, after that, no any reassignment will happen, before the case class goes into any collections or do equality comparison, will that still mess up the equality behaviors?
Case class equality is based solely on its primary constructor attributes, whether they're var or val (yes, you can make them var by giving an explicit var to override the implied val that case class constructor args possess.) Adding properties in the body of a case class does not influence the compiler-generated equals(other: Any) method.
Witness:
package rrs.scribble
object CCVarEq
{
case class CC1(i: Int, j: Float, var k: Double)
case class CC2(i: Int, j: Float, var k: Double) {
var l = math.Pi
}
def show {
val cc11 = CC1(1, 2.0f, 3.0)
val cc12 = CC1(1, 2.0f, 3.0)
val cc21 = CC2(1, 2.0f, 3.0); cc21.l = math.E
val cc22 = CC2(1, 2.0f, 3.0)
printf("cc11 == cc12: %s%n", cc11 == cc12); cc12.k = math.Pi * math.E
printf("cc11 == cc12: %s%n", cc11 == cc12)
printf("cc21 == cc22: %s%n", cc21 == cc22)
}
}
In the REPL:
scala> import rrs.scribble.CCVarEq._
import rrs.scribble.CCVarEq._
scala> show
cc11 == cc12: true
cc11 == cc12: false
cc21 == cc22: true
And all jamie's points about concurrency are valid, too.
It's not that simple. You update a case class var on one thread, and another thread is performing an equality check. Unless the var is volatile, it's plausible that the change to the var won't be propagated to the other thread before the equality check is performed. So you could have race conditions. Regardless if this happens only once or many times.
Is the value to be changed once undefined up to that point? If so, use a lazy val. It has a synchronization concern that could slow high-performance code, but would otherwise meet your use case.

Scala Properties Question

I'm still learning Scala, but one thing I thought was interesting is that Scala blurs the line between methods and fields. For instance, I can build a class like this...
class MutableNumber(var value: Int)
The key here is that the var in the constructor-argument automatically allows me to use the 'value' field like a getter/setter in java.
// use number...
val num = new MutableNumber(5)
num.value = 6
println(num.value)
If I want to add constraints, I can do so by switching to using methods in place of the instance-fields:
// require all mutable numbers to be >= 0
class MutableNumber(private var _value: Int) {
require(_value >= 0)
def value: Int = _value
def value_=(other: Int) {
require(other >=0)
_value = other
}
}
The client side code doesn't break since the API doesn't change:
// use number...
val num = new MutableNumber(5)
num.value = 6
println(num.value)
My hang-up is with the named-parameter feature that was added to Scala-2.8. If I use named-parameters, my API does change and it does break the api.
val num = new MutableNumber(value=5) // old API
val num = new MutableNumber(_value=5) // new API
num.value = 6
println(num.value)
Is there any elegant solution to this? How should I design my MutableNumber class so that I can add constraints later on without breaking the API?
Thanks!
You can use the same trick that case classes do: use a companion object.
object Example {
class MutableNumber private (private var _value: Int) {
require (_value >= 0)
def value: Int = _value
def value_=(i: Int) { require (i>=0); _value = i }
override def toString = "mutable " + _value
}
object MutableNumber {
def apply(value: Int = 0) = new MutableNumber(value)
}
}
And here it is working (and demonstrating that, as constructed, you must use the object for creations, since the constructor is marked private):
scala> new Example.MutableNumber(5)
<console>:10: error: constructor MutableNumber cannot be accessed in object $iw
new Example.MutableNumber(5)
^
scala> Example.MutableNumber(value = 2)
res0: Example.MutableNumber = mutable 2
scala> Example.MutableNumber()
res1: Example.MutableNumber = mutable 0
Thanks for the answer! As an aside, I think the Scala-guys might be aware that there's an issue:
What's New in Scala 2.8: Named and Default Parameters
...
Until now, the names of arguments were a somewhat arbitrary choice for library developers, and weren't considered an important part of the API. This has suddenly changed, so that a method call to mkString(sep = " ") will fail to compile if the argument sep were renamed to separator in a later version.
Scala 2.9 implements a neat solution to this problem, but while we're waiting for that, be cautious about referring to arguments by name if their names may change in the future.
http://www.artima.com/scalazine/articles/named_and_default_parameters_in_scala.html
class MutableNumber {
private var _value = 0 //needs to be initialized
def value: Int = _value
def value_=(other: Int) {
require(other >=0) //this requirement was two times there
_value = other
}
}
you can modify all members of any class within curly braces
val n = new MutableNumber{value = 17}