Traits are halfway between Java interfaces and abstract classes.
· Like abstract classes, traits may contain concrete members, but may not be instantiated.
· As with interfaces, a class can inherit the members of multiple traits.
· Classes can extend traits
class C extends Trait1 { ... }
· Traits can extend classes and other traits
trait T1 extends Class1 { ... }
trait T2 extends Trait1 { ... }
· Classes, objects, and traits can mix in traits
class C extends Trait1 with Trait2 with Trait3 { ... }
val obj = new Class1() with Trait1 with Trait2 with Trait3 { ... }
Like most programming languages, Acorn distinguishes between expressions (things that can be executed) and values (results of executing expressions). Jawa formalizes this distinction by defining two traits:
trait Value { }
trait Expression {
def execute: Value
}
Numbers and Booles are both, expressions and values, When executed they return themselves! In future versions of Acorn there may be additional expression-value hybrids such as strings and characters. Acorn calls all such hybrids literals:
trait Literal extends Value with Expression {
def execute = this
}
class Number (val value: Double) extends Literal { }
class Boole (val value: Boolean) extends Literal { }
By contrast, a sum is an expression but not a value:
class Sum(operand1: Expression, operand2: Expression) extends
Expression {
def execute = {
val arg1 = operand1.execute
val arg2 = operand2.execute
if (!arg1.isInstanceOf[Number] ||
!arg2.isInstanceOf[Number]) {
throw new Exception("sum
inputs must be numbers")
}
val num1 = arg1.asInstanceOf[Number]
val num2 = arg2.asInstanceOf[Number]
new Number(num1.value + num2.value)
}
}
In UML we must introduce a special stereotype to represent traits:
Delegation chains are useful when we want to add behavior to an object.
Assume Delegate is a trait with a concrete do-nothing method:
trait
Delegate {
def delegate() {}
}
An
Entity class inherites the do-nothing delegate method, which it calls:
class
Entity extends Delegate {
def doSomething() {
println("calling
Entity.doSomething")
delegate()
}
}
Next
we define three sub-traits. Each prints a unique message, then calls super.delegate:
trait
Delegate1 extends Delegate {
override def delegate() {
println("calling
Delegate1.delegate")
super.delegate()
}
}
//Delegte2,
Delegate3, etc.
Scala
allows objects to inherit from traits, too:
val
test1 = new Entity() with Delegate1 with Delegate2 with Delegate3
test1.doSomething()
val test2 = new Entity() with Delegate3 with Delegate1 with Delegate2
test2.doSomething()
Here's
the output. Note the delegation order:
calling
Entity.doSomething
calling Delegate3.delegate
calling Delegate2.delegate
calling Delegate1.delegate
calling Entity.doSomething
calling Delegate2.delegate
calling Delegate1.delegate
calling Delegate3.delegate