Question

I've got a class from a library (specifically, com.twitter.finagle.mdns.MDNSResolver). I'd like to extend the class (I want it to return a Future[Set], rather than a Try[Group]).

I know, of course, that I could sub-class it and add my method there. However, I'm trying to learn Scala as I go, and this seems like an opportunity to try something new.

The reason I think this might be possible is the behavior of JavaConverters. The following code:

class Test {
    var lst:Buffer[Nothing] = (new java.util.ArrayList()).asScala
}

does not compile, because there is no asScala method on Java's ArrayList. But if I import some new definitions:

class Test {
    import collection.JavaConverters._
    var lst:Buffer[Nothing] = (new java.util.ArrayList()).asScala
}

then suddenly there is an asScala method. So that looks like the ArrayList class is being extended transparently.

Am I understanding the behavior of JavaConverters correctly? Can I (and should I) duplicate that methodology?

Was it helpful?

Solution

Scala supports something called implicit conversions. Look at the following:

val x: Int = 1
val y: String = x

The second assignment does not work, because String is expected, but Int is found. However, if you add the following into scope (just into scope, can come from anywhere), it works:

implicit def int2String(x: Int): String = "asdf"

Note that the name of the method does not matter.

So what usually is done, is called the pimp-my-library-pattern:

class BetterFoo(x: Foo) {
  def coolMethod() = { ... }
}

implicit def foo2Better(x: Foo) = new BetterFoo(x)

That allows you to call coolMethod on Foo. This is used so often, that since Scala 2.10, you can write:

implicit class BetterFoo(x: Foo) {
  def coolMethod() = { ... }
}

which does the same thing but is obviously shorter and nicer.

So you can do:

implicit class MyMDNSResolver(x: com.twitter.finagle.mdns.MDNSResolver) = {
  def awesomeMethod = { ... }
}

And you'll be able to call awesomeMethod on any MDNSResolver, if MyMDNSResolver is in scope.

OTHER TIPS

This is achieved using implicit conversions; this feature allows you to automatically convert one type to another when a method that's not recognised is called.

The pattern you're describing in particular is referred to as "enrich my library", after an article Martin Odersky wrote in 2006. It's still an okay introduction to what you want to do: http://www.artima.com/weblogs/viewpost.jsp?thread=179766

The way to do this is with an implicit conversion. These can be used to define views, and their use to enrich an existing library is called "pimp my library".

I'm not sure if you need to write a conversion from Try[Group] to Future[Set], or you can write one from Try to Future and another from Group to Set, and have them compose.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top