Scala/Traits
Interfaces
[edit | edit source]In Java and a number of other languages, there is the concept of an interface: an interface describes a set of methods and properties that an implementing class must have.
For instance, imagine you are creating classes to represent different types of people. You decide that all people must be contactable by e-mail and have names and genders. You can define an interface called something like Person:
interface Person {
String name;
Gender gender;
void sendEmail(String subject, String body);
}
When you create classes, you can implement the Person interface.
class Student implements Person {
public String name;
public Gender gender;
public void sendEmail(String subject, String body) {
// ...
}
}
Why do this? Simple: elsewhere, you can use this Person interface as a type. You can create a method which takes a Person type as an argument. This is basically expressing a contract: you aren't interested in the class of the object, only whether it meets the requirements of the interface.
Here is one major difference that Scala has from Java: there are no interfaces. There is no interface keyword.
Yes, even though Scala is primarily a JVM language and often touted as a "better Java", it doesn't have interfaces.
Scala has something better: traits.
What are traits?
[edit | edit source]To understand traits, you need to understand why Java has interfaces. Other languages like Python and C++ don't need interfaces because they have multiple inheritance. But multiple inheritance is subject to the diamond problem: what if you are defining a class that inherits two methods with the same type signature from two different classes? Which one should be used? It was to avoid the complexity and bugs that flow from multiple inheritance that the designers of Java decided not to implement multiple inheritance and instead have single inheritance with interfaces to sugar the pill and make the type system slightly more flexible.
Scala presents a half-way point between full multiple inheritance and Java's single inheritance with interfaces model by giving you traits.
Traits give you the ability to recreate interfaces as Java does but also allows you to go further.
Traits as interfaces
[edit | edit source]At the very basic level, traits let you recreate the interfaces and implementation model of Java. Here is how you would create the above example in Scala using traits.
trait Person {
var name: String
var gender: Gender
def sendEmail(subject: String, body: String): Unit
}
(Unit is Scala's version of void, remember.)
You can now implement the Person trait:
class Student extends Person {
var name: String
var gender: Gender
def sendEmail(subject: String, body: String): Unit = {
// ...
}
}
Implement it too!
[edit | edit source]You can use traits to provide the default implementation of the interface as well. Although the idea of "interface with implementation" sounds slightly contradictory: if you are defining the implementation, how can you say you are simply defining the interface? The answer is simple: traits follow the semantics of interfaces in Java but extend them with the option of providing a default implementation.
trait Quacking {
def quack() = {
println("Quack quack quack!")
}
}
A class can override the quack method if it wants to, but doesn't have to. We can see the Quacking trait in use in the next section.
Singletons can use traits too
[edit | edit source]Scala can use traits when instantiating objects or with singletons. Instantiating a class with a trait basically creates a one-off singleton. This enables you to have something very much like Ruby's module mixins but with static type checking.
class Duck {
val version = "ACME Inc. Generic Duck v1.0"
}
val aDuck = new Duck with Quacking
aDuck.quack()
Here we have created an object that has the class of Duck but also "mixes in" the Quacking trait. It has both types, and can thus be given to a method that takes 'Quacking' as the type.
A more practical refrain
[edit | edit source]If you are a Java or C# programmer, you should be able to think of plenty of situations where you might want an interface. In those situations, you can probably use traits.
But what about an example of where you might want to use this kind of mixin pattern?
A simple one: creating fake objects for testing. If you are creating unit tests, you sometimes want to test the interface of an object whose function it is to call an outside service like a database, web service etc. You still want to know whether the API of a particular class works without having to send out computationally expensive calculations to your database or financially expensive transactions to your credit card payment processor. Traditionally, the answer to this is to use mocking and stubbing patterns, possibly a mocking library like JMock.
But Scala provides a simpler solution to this.
Imagine a class that called out to a web service which we'll call FooApi.
class FooApi {
def getFromApi() = http(blargh)
}
getFromApi uses HTTP to get some XML from a remote server on the Internet.
Testing the getFromApi method is expensive because it makes a call out across the Internet. Your unit tests aren't there to test whether the Internet is working or whether the servers are working. We should probably make it so we can test this without needing to have an Internet connection.
In our test file, we can define a trait to do just this.
trait FooMocker extends FooApi {
override def getFromApi() = <response><from /><the /><api /></response>
}
You can replace the XML response with whatever you expect to get back from the API.
Then you can write the test (example code uses the Specs testing library, but you can use any unit testing library you prefer):
object FooApiTest extends Specification {
"FooApi" should {
"return from the API" in {
var foo = new FooApi with FooMocker
foo.getFromApi() mustBe <response><from /><the /><api /></response>
}
}
}
Here, using traits, the "mixins" pattern lets us replace the black box methods within our code with mocks. Traits can be used for a wide variety of functions and can lead to less repetition in one's code.