Scala - How to add new methods to existing classes

Update: This article was written for Scala 2.9. Things changed a little bit in Scala 2.10, so see this new article, Creating implicit methods in Scala 2.10, for correct examples for 2.10 and newer versions of Scala.

A cool thing about implicit conversions in Scala is that they let you add new methods to existing classes, including existing Java and Scala classes such as String, File, and so on.

For instance, let's say you want to add a method to the Java String class, and this new method will increment each character in the String by one byte. The first thing you do is define a Scala class that has a method that implements the behavior you want:

class BetterString(val s: String) {
  def increment = s.map(c => (c + 1).toChar)
}

In that class I created a new method named increment, which uses the String the class is created with, and returns a new String, where each character in the String has been increased by one byte ('A' becomes 'B', 'B' becomes "C', and so on).

Next, I define an implicit conversion that ties my new class to the existing Java String class:

implicit def stringToString(s: String) = new BetterString(s)

Notice that the stringToString method is a very normal looking Scala function, but because it's preceded by the implicit keyword, some very cool "magic" happens. Here's how this works:

  • Because my implicit method stringToString accepts a String as input, Scala is smart enough to know that every time I have a String value, such as a String literal, it should look in my BetterString class for additional methods that accept a String as a parameter. (The implicit keyword has a lot of power.)
  • In my BetterString class I've defined my increment method, and it does the work of walking through each character in a String, incrementing that Char by one byte, and then returning the entire sequence of new characters when it's finished iterating over the String.

As a result, I've tied my increment method to the String class, so I can now type something like this:

"foo".increment

Testing it in the Scala REPL

If you're new to implicit conversions, I'm sure that's still a little hard to understand, so let's take a look at how this works in the Scala REPL. First we define our class:

scala> class BetterString(val s: String) {
     | def increment = s.map(c => (c + 1).toChar)
     | }
defined class BetterString

That looks just like a normal Scala class, no magic there. Next we define our stringToString function:

scala> implicit def stringToString(s: String) = new BetterString(s)
stringToString: (s: String)BetterString

Again, except for the implicit keyword, that looks like a normal function.

And now it's time for the big test. Let's create a String literal and then see if our increment method will work:

scala> "HAL".increment
res0: String = IBM

As you can see from that last line, I typed in the String "HAL", then followed it with my increment method, and that method returned a new String "IBM", which is the result of incrementing each character by one byte.

Make it your own: Add more method to existing classes

To help you understand what's happening here, I encourage you to add more methods to my Scala class and see if you can make them work. For instance, try implementing a "backwards" method, and see if you can make that accept a String, and print the String in reverse order. (Note that there's already a String method named reverse, and you can use that in your "backwards" method.

As another test, try to implement a method that has the same name as an existing String method. For instance, try to define and then use a method named reverse and see what happens.

Summary

As you've seen, you can extend existing classes in Scala using implicit conversions. To implement this recipe of adding new functionality to existing classes, you:

  1. Create a new class that contains the new method(s) you want. In my case I added the increment method to the String class.
  2. Use the "implicit def" function definition to create your implicit conversion, mapping the conversion to your new class.

Two more notes before I go:

  1. Knowing about this capability is good, you can do some really nice things in your code; but I wouldn't use it too often. There is a little "magic" to how this works, and that magic can be confusing.
  2. I forgot to mention it earlier, but there's nothing special about the name of your implicit conversion function. I named my function stringToString, but I could have named it aToB, or foo, or anything else. That being said, in the long run you're better off giving your functions meaningful names, and most people use this "sourceToTarget" naming convention.

I hope this example of how add new methods to existing classes in Scala has been helpful.

Add new comment

The content of this field is kept private and will not be shown publicly.

Anonymous format

  • Allowed HTML tags: <em> <strong> <cite> <code> <ul type> <ol start type> <li> <pre>
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.