Question

Question
In Scala, is there a way to write a match clause which matches an object by it's class, but does not match any extending classes?

Motivation
It may seem pedantic.. it is.
Anyway, I thought of it when I saw the equals function generated by IntelliJ:

class Position(val x: Float, val y: Float) {

  def canEqual(other: Any): Boolean = other.isInstanceOf[Position]

  override def equals(other: Any): Boolean = other match {
    case that: Position =>
      (that canEqual this) &&
        x == that.x &&
        y == that.y
    case _ => false
  }

  // generated hashCode omitted for brevity

}

Here we can see that other is class matched to be a Position, and then canEqual is used to put an upper bound on the match. That is, other could still be, say, a Position3D, which shouldn't equal a Position even if the x and y coordinates are the same.

This makes sense, but I wonder if some form of Scala wizardry would let us explicitly match Position classes and not subclasses.

Était-ce utile?

La solution

There's a reason the auto-generated equals method doesn't do what you're suggesting. The canEqual convention is a common and well-documented one, because it allows for handling a broader set of common use cases.

You don't always want to exclude all sub classes when implementing an equals method, only ones that override equals. If, as you described, there was a Position3D class extending Position, and Position3D had an additional field z that was part of the Position3D equals method, then of course that Position3D should not be equal to any normal 2D position.

But what if for some reason a user wants to instantiate an anonymous subclass of Position?

val pos = new Position(1,2){}

or

trait Foo
val pos = new Position(1,2) with Foo

This position should probably be considered equal to new Position(1,2). Disallowing this equality could in some situations be tedious, if the mixed-in functionality is trivial or just for some particular local-scope use case. To require, as the other response suggested, other.getClass == classOf[Position], a client API mixing in a trait or for some reason creating an anonymous class might find they're creating positions that are not even equal to themselves.

Consider this code:

class Position(val x:Int, val y:Int) {
  override def equals(other:Any) = other match {
    case o:Position if o.getClass==classOf[Position] && o.x==x && o.y==y => true
    case _ => false
  }
}
trait Foo
val p1 = new Position(1,2) with Foo
val p2 = new Position(1,2) with Foo
p1 == p2

the result is false

see: http://www.artima.com/lejava/articles/equality.html

Autres conseils

Try:

other match {
    case that: Position if that.getClass == classOf[Postion] => // or just getClass
        (that canEqual this) &&
            x == that.x &&
            y == that.y
    case: that: Postion => false //this case can be removed
    case _ => false
 }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top