Skip to content

Rewrote traits tour #713

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 11, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 60 additions & 32 deletions tutorials/tour/_posts/2017-02-13-traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,74 @@ categories: tour
num: 5
next-page: mixin-class-composition
previous-page: classes
assumed-knowledge: expressions, classes, generics, objects, companion-objects
---

Similar to interfaces in Java, traits are used to define object types by specifying the signature of the supported methods. Like in Java 8, Scala allows traits to be partially implemented; i.e. it is possible to define default implementations for some methods. In contrast to classes, traits may not have constructor parameters.
Here is an example:

Traits are used to share interfaces and fields between classes. They are similar to Java 8's interfaces. Classes and objects can extend traits but traits cannot be instantiated and therefore have no parameters.

## Defining a trait
A minimal trait is simply the keyword `trait` and an identifier:

```tut
trait HairColor
```

Traits become especially useful as generic types and with abstract methods.
```tut
trait Similarity {
def isSimilar(x: Any): Boolean
def isNotSimilar(x: Any): Boolean = !isSimilar(x)
trait Iterator[A] {
def hasNext: Boolean
def next(): A
}
```

This trait consists of two methods `isSimilar` and `isNotSimilar`. While `isSimilar` does not provide a concrete method implementation (it is abstract in the terminology of Java), method `isNotSimilar` defines a concrete implementation. Consequently, classes that integrate this trait only have to provide a concrete implementation for `isSimilar`. The behavior for `isNotSimilar` gets inherited directly from the trait. Traits are typically integrated into a [class](classes.html) (or other traits) with a [mixin class composition](mixin-class-composition.html):


Extending the `trait Iterator[A]` requires a type `A` and implementations of the methods `hasNext` and `next`.

## Using traits
Use the `extends` keyword to extend a trait. Then implement any abstract members of the trait using the `override` keyword:
```tut
class Point(xc: Int, yc: Int) extends Similarity {
var x: Int = xc
var y: Int = yc
def isSimilar(obj: Any) =
obj.isInstanceOf[Point] &&
obj.asInstanceOf[Point].x == x
trait Iterator[A] {
def hasNext: Boolean
def next(): A
}
object TraitsTest extends App {
val p1 = new Point(2, 3)
val p2 = new Point(2, 4)
val p3 = new Point(3, 3)
val p4 = new Point(2, 3)
println(p1.isSimilar(p2))
println(p1.isSimilar(p3))
// Point's isNotSimilar is defined in Similarity
println(p1.isNotSimilar(2))
println(p1.isNotSimilar(p4))


class IntIterator(to: Int) extends Iterator[Int] {
private var current = 0
override def hasNext: Boolean = current < to
override def next(): Int = {
if (hasNext) {
val t = current
current += 1
t
} else 0
}
}
```

Here is the output of the program:


val iterator = new IntIterator(10)
iterator.next() // prints 0
iterator.next() // prints 1
```
true
false
true
false
This `IntIterator` class takes a parameter `to` as an upper bound. It `extends Iterator[Int]` which means that the `next` method must return an Int.

## Subtyping
Subtypes of traits can be used where a the trait is required.
```tut
import scala.collection.mutable.ArrayBuffer

trait Pet {
val name: String
}

class Cat(val name: String) extends Pet
class Dog(val name: String) extends Pet

val dog = new Dog("Harry")
val cat = new Cat("Sally")

val animals = ArrayBuffer.empty[Pet]
animals.append(dog)
animals.append(cat)
animals.foreach(pet => println(pet.name)) // Prints Harry Sally
```
The `trait Pet` has an abstract field `name` which gets implemented by Cat and Dog in their constructors. On the last line, we call `pet.name` which must be implemented in any subtype of the trait `Pet`.