In the previous post, we looked at implicit parameters; parameters that will be automatically passed values annotated as implicit
. In this post, we’ll take a look at implicit functions and how they can be useful to convert things of one type to things of another.
Implicit Functions
Implicit functions will be called automatically if the compiler thinks it’s a good idea to do so. What that means is that if your code doesn’t compile but would, if a call was made to an implicit function, Scala will call that function to make it compile. They’re typically used to create implicit conversion functions; single argument functions to automatically convert from one type to another.
For example, the following function allows you to convert a Scala function into a instance of the Java 8 Consumer
single argument method but still use Scala’s concise syntax.
|
You can avoid having to write clunky anonymous class instantiation when interfacing with Java and so mimic Java’s lambda syntax. So rather than having to use the longhand version like this.
|
You can write this, where we just pass a Scala function to Java’s forEach
method.
|
The argument to forEach
is actually a function of type Element => Unit
. Scala recognises that the toConsumer
method could convert this into a Consumer[Element]
and does so implicitly.
|
Which is basically short-hand for this.
|
Another Example
If we have a button on we web page that we’d like to find using Web Driver, we’d normally write something like the following, using a “locator” to locate it by id
attribute.
|
It doesn’t take into account that the element might not be there when we call it (for example, when our UI uses ajax and adds the button asynchronously) and it’s also a bit verbose. We can use an implicit function to address both of these issues.
The fragment below uses the WebDriverWait
class to wait for a UI element to appear on the screen (using findElement
to check and retrying if necessary) and so smooths out the asynchronous issues.
|
It’s also an implicit function designed to convert a By
locator into a WebElement
. It means we can write something like the following where button
is no longer a WebElement
, but a By
.
|
Without the implicit waitForElement
function, the code wouldn’t compile; By
doesn’t have a click
method on it. With the implicit function in scope however, the compiler works out that calling it (and passing in create
as the argument), would return something that does have the click
method and would compile.
Single Arguments Only Please
Now there’s one little bit I’ve brushed over here; namely how the WebDriver
driver
instance is made available. The example above assumes it’s available but it’d be nicer to pass it into the function along with locator
. However, there’s a restriction of passing only a single argument into an implicit function. The answer is to use a second argument (using Scala’s built in currying support). By combining implicit parameters the we saw in the previous post, we can maintain the elegant API.
|
So the full example would look like this; making driver
an implicit val
means we can avoid a call to button.click()(driver)
.
|
Roundup
You can see from the examples above that implicit functions (and often combining them with implicit values) can make for succinct and more readable APIs. Next we’ll look at implicit classes.
If you’re interested in more Java bridge implicits like toConsumer
, check out this gist.