11

My intention is to change the behaviour of the == method in String to call equalsIgnoreCase.

This code

implicit class LowerCase(s: String) {
      override def ==(that: LowerCase) = that.equalsIgnoreCase(this)
}

results in this error

error: type mismatch;
 found   : MyClass.LowerCase
 required: String
      override def ==(that: String) = that.equalsIgnoreCase(this)
Hanxue
  • 12,243
  • 18
  • 88
  • 130
  • What do you mean by "does not work"? Do you get errors? Does it behave differently from what you expect? What did you expect and what does it do in reality, how does it differ from what you expected? – Jesper Dec 20 '13 at 08:22
  • Yes, added compiler error message – Hanxue Dec 20 '13 at 08:27

1 Answers1

18

In case there is already such method in class scala compiler would not search for implicit conversion.

Note that your implementation is invalid, it should be:

implicit class LowerCase(val s: String) {
      def ==(that: LowerCase) = that.s.equalsIgnoreCase(this.s)
}

Note that it's still useless.

In case you want to use it as Map's key you should specify type manually:

implicit class LowerCase(val s: String) {
      // Use `equals`, not `==`
  override def equals(that: Any) = that match {
    case t: LowerCase => t.s.equalsIgnoreCase(this.s)
    case _ => false
  }

  // Don't forget to override hashCode for Map and Set
  override lazy val hashCode = s.toLowerCase.hashCode

  override def toString() = s
}

scala> Set[LowerCase]("A", "a", "b")
res0: scala.collection.immutable.Set[LowerCase] = Set(A, b)

If you want to use this method with variables like this "a" == "A" you should use another method name:

implicit class LowerCase(val s: String) extends AnyVal {
      def ===(that: String) = s.equalsIgnoreCase(that)
}

scala> "a" === "A"
res0: Boolean = true
senia
  • 37,745
  • 4
  • 88
  • 129
  • 1
    Is there a way to override a class method implicitly, i.e. without extending and manually overriding the superclass' method? – Hanxue Dec 20 '13 at 08:29
  • There are still errors: remove `override`, and the constructor parameter needs to be a `val`. But even then it won't work, because of what you mentioned (Scala has no reason to search for an implicit conversion). – Jesper Dec 20 '13 at 08:32
  • Thank you very much senia, @Jesper. Indeed, defining a new method is much cleaner. – Hanxue Dec 20 '13 at 09:09
  • 1
    @hanxue: Note that there is already a method `===` (and `=/=`) in `scalaz` (see [Scalaz cheat sheet](http://eed3si9n.com/ja/scalaz-cheat-sheet)), and you could override it's behavior using `implicit object IgnoreCaseEqual extends new Equal[String] { def equal(s1: String, s2: String) = s1.equalsIgnoreCase(s2) }`, but you'll have to hide a default implementation like this: `import Scalaz.{stringInstance => _, _}`. – senia Dec 20 '13 at 09:14
  • Thanks senia. Not using scalaz for this particular application because it is really simple. Your answer is exactly what I needed :) – Hanxue Dec 20 '13 at 10:02