Search code examples
scalagenericswrapperequals-operator

Comparing wrapper object of generic type to with everything that is comparable to inner type?


I am trying to build a wrapper around data saved in a redis database. What I want to do is access it as normal as possible as if I have a scala value of some type. It works, except for comparing it to anything (with == or similar).
So I want to be able to compare it to objects of the inner type.

Best I'll give example code first. This is a rough abstraction of my wrapper:

case class RedisObjectValue[T](name: String, default: T) {
  def :=(value: T): Boolean = {
    redisMagic.set(name, value)
  }

  def get: T = {
    redisMagic.get[T](name).getOrElse(default)
  }

  override def equals(o: Any) = o match {
    case ov: RedisObjectValue[T] =>
      if (this.hashCode() == ov.hashCode()) true // Same reference
      else this.get == ov.get
    case v =>
      // If we don't compare to another RedisObjectValue, let the equals method of data type handle it
      this.get.equals(v)
  }
}

I have an implicit conversion set up in the companion object so that I can use the wrapper wherever I would use the base type.

object RedisObjectValue {
  implicit def objectValueToValue[T](ov: RedisObjectValue[T]): T = {
    ov.get
  }
}

So, as I said, everything works.
Except the comparing stuff. For that, let's say I have a class Player two values, userId and name.

class Player {
  val userId = RedisObjectValue("userId", 0)
  val name = RedisObjectValue("player", "Unknown")
}

And now I have a list of players and want to filter to get all players with name "Unknown".

list.filter(_.name == "Unknown")

Which does not work. If I extend the filter filter call and write a function, it tells me in IntelliJ "Comparing unrelated types".
And yeah, I understand what it is telling me, but I want to solve that. I mean I can easily compare Long to Int and similar stuff, so there must be a way to make them comparable, right?

In the code above I have even written the equals function which uses the comparison to the inner type, but it seems like it is not used.

Of course, I can always call .get on the values, like players.filter(_.name.get == "Unknown"), but that's a bit dirty and I would love to avoid that.

EDIT: Found the real problem after some analysis

Old text still above for reading, I will explain the problem here now.
I have a snippet which shows what is not working: https://ideone.com/AmQrkH

The Problem: The RedisObjectValue is defined as Long. When I am comparing it now with

players.filter(_.userId == 2)

for example, it doesn't give any results, even if there are players with userId 2, or to be more exact: 2l.

For direct Long it's not a problem.

players.filter(_._id == 2)

is working.
So is there any fix for that, so that comparable instances of classes to T can also be compared to RedisObjectValue[T], and not just T itself?


Solution

  • Replace this.get.equals(v) with get == v. (Removing this is unrelated, just a good idea in general, the main thing is to use == instead of equals).

    Consider this:

     scala> 2 equals 2L
     res79: Boolean = false
    
     scala> 2 == 2L
     res80: Boolean = true