umbc cmsc 331 traits in scala. 2 basic properties traits are used to define object types by...
TRANSCRIPT
2
Basic Properties
Traits are used to define object types by specifying the signature of the supported methods.
Unlike Java, Scala allows traits to be partially implemented; i.e. it is possible to define default implementations for some methods.
Traits may not have constructors
2
3
Simple Example Using Extends3
trait Similarity { def isSimilar(x: Any): Boolean def isNotSimilar(x: Any): Boolean = !isSimilar(x)}
This trait consists of two methods isSimilar and isNotSimilar isSimilar is abstract isNotSimilar is concrete but written in terms of isSimilar
Classes that integrate this trait only have to provide a concrete implementation forisSimilar, isNotSimilar gets inherited directly from the trait
4
What’s the output? (Part 1)4
class Point(xc: Int, yc: Int) extends Similarity {var x: Int = xcvar y: Int = yc def isSimilar(obj: Any) = obj.isInstanceOf[Point] && obj.asInstanceOf[Point].x == x}
5
What’s the output? (Part 2)5
object TraitsTest extends Application {val p1 = new Point(2, 3)val p2 = new Point(2, 4)val p3 = new Point(3, 3) println(p1.isNotSimilar(p2)) println(p1.isNotSimilar(p3)) println(p1.isNotSimilar(2))}
6
Mixing in a Trait Using With6
Consider the running example on pgs 213-215 of Odersky
Since Philosophical and HasLegs are Traits and Animal is a class we may write:
class Frog extends Animal with Philosophical with HasLegs {override def toString = “green”override def philosophize =println(“Itain’t easy being “+ toString +”!”)
}
7
The Ordered Trait7
Thin vs. Rich Interfaces Rich has many methods (easier in theory for client) Thin has fewer – easier for implementer (see Odersky)
The Ordered Trait If Interface is rich then must supply <, > , <=, >= instead
want a trait with a single method (here is an example)
class Rational (n : Int, d : Int) extends Ordered[Rational]
{ def compare (that: Ratioonal) = (this.numer * that.denom)– (that.numer * this.denom)
}
8
Traits with Stackable Modifications8
Use to stack modifications to a classConsider the IntQueue class from Odersky
(pg 223)
abstract class IntQueue {def get () : Intdef put(x : Int)
}
Now we’ll build a concrete class atop of it
9
An implementation9
import scala.collection.mutable.ArrayBuffer
class BasicQueue extends IntQueue{private val buf = new ArrayBuffer[int]
def get () : buf.remove(0) def put(x : Int) { buf += x }
}
10
A Trait Useful For the Queue10
trait Doubling extends IntQueue {abstract override def put (x : Int) { super.put(2*x) }
}
Can only be used with IntQueues
This refers to the class that actually
uses the trait
val queue = new BasicIntQueue with Doublingqueue.put(10)queue.get()
11
Two Additional Traits11
trait Incrementing extends IntQueue {abstract override def put (x : Int) { super.put(x+1) }
}
trait Filtering extends IntQueue {abstract override def put (x : Int) { if (x> = 0) super.put(x)}
}
12
trait Similar{ def isSimilar(x: Any): Boolean def isNotSimilar(x: Any): Boolean = !isSimilar(x)}
case class MyInt (x :Int) extends Similar { def isSimilar(x :Int) : Boolean = m.isInstance[MyInt] && m.asInstanceOf[MyInt].x = x}
trait Similar{ def isSimilar(x: Any): Boolean def isNotSimilar(x: Any): Boolean = !isSimilar(x)}
case class MyInt (x :Int) extends Similar { def isSimilar(x :Int) : Boolean = m.isInstance[MyInt] && m.asInstanceOf[MyInt].x = x}
Upper Bounds(1)12
14
case class ChangeEvent[OnType](on: OnType)
trait Observer[T] { this: T with Observer[T] =>
type ChangeHandler = { def changed(c: ChangeEvent[T with Observer[T]]): Unit }
private var observers: List[ChangeHandler] = Nil def addObserver(c: ChangeHandler) = synchronized { observers = c :: observers } … }}
Involved Example Using Traits14
The param is the type of thing that changed
The type of thing for this this in the trait is just the type of the thing we’ve mixed Observer into. We don’t know T till runtime. Using thisthis as shown is called a self type – see Odersky pg 537
A type that matched anything with that method and signature
15
Rest of Trait Definition15
trait Observer[T] { … def removeObserver(c: ChangeHandler)= synchronized { observers -= c }
protected def updateObservers() = synchronized { val ch = ChangeEvent(this) Observers.foreach(i =>i.changed(ch)) }}
A ChangeEvent has been made ( a case class) , tell each observer about the change.
16
class Shibby extends Observer[Shibby] { private var _count = 0 def count = synchronized{_count} def inc = synchronized { _count += 1 updateObservers() }}
val s = new Shibby
Testing16
This class generates the events and then tells the Observers about it
17
Testing17
object Dibby { def changed(c: ChangeEvent[Shibby]) { println(“Dibby changed: + c.on.count)}}
object Doo{ def changed(c: ChangeEvent[Shibby]) { println(“Doo changed: + c.on.count)}}
These classes respond to the changes
18
Observer Get Events18
s.addObserver(Dibby)s.addObserver(Doo)
s.incDibby changed 1Doo changed 1
s.removeObserver(Dibby)s.incDoo changed 2