7

I'm reading through Programming in Scala. It says:

You can redefine the behavior of == for new types by overriding the equals method, which is always inherited from class Any. The inherited equals, which takes effect unless overridden, is object identity, as is the case in Java. So equals (and with it, ==) is by default the same as eq, but you can change its behavior by overriding the equals method in the classes you define. It is not possible to override == directly, as it is defined as a final method in class Any. That is, Scala treats == as if was defined as follows in class Any:

final def == (that: Any): Boolean = 
  if (null eq this) (null eq that) else (this equals that)

But this isn't jibing with what I'm seeing in scala 2.9.1, where it seems like:

  • == doesn't seem to default to equals
  • I can override == directly (without complaint from the compiler, no override needed).

So it seems to me like either:

  • I'm doing it wrong - this definition of Rational gives

    % scala                                                                   
    Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29).
    Type in expressions to have them evaluated.
    Type :help for more information.
    
    scala> Rational(1) == Rational(1)
    res0: Boolean = false
    
    scala> Rational(1) equals Rational(1)
    res1: Boolean = true
    
  • or I'm reading an out of date version of the book, and things have changed.

What's going on?

rampion
  • 87,131
  • 49
  • 199
  • 315

1 Answers1

21

You are making a very understandable mistake--you are trying to write a type-safe equals (i.e. def equals(r: Rational)) instead of a generic equals (i.e. override def equals(a: Any)).

So instead of overriding equals--note that you don't need the override keyword!--you are creating another method by overloading the type parameters, and then having two equals methods, one which takes Rational and one which takes Any. Same thing with ==; only the Any-parameterized method cannot be overridden.

To get the behavior consistent with Java (and the Scala library), you'd need to rewrite equals as something like

override def equals(a: Any) = a match {
  case r: Rational => numer == r.numer && denom == r.demon
  case _ => false
}
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • Awesome. This bites me every time I return to JVM land. I'll learn it sooner or later. Is there anyway I can drop the `a match` and just assign `equals` an anonymous function? `override def equals : Any => Boolean = { case r : Rational /* ... */ }` doesn't quite work... – rampion Mar 24 '12 at 17:32
  • 2
    @rampion - No, you have to match the type signature exactly. `equals: Any=>Boolean` means that it returns a function (i.e. an instance of a `Function1[Any,Boolean]` class). Even though this is conceptually equivalent in some sense, it is not the exact same method, so it won't work. – Rex Kerr Mar 24 '12 at 17:38