- advanced types
- singleton type
def setTitle(title: String): this.type = { ...; this } // for subtypes
def set(obj: Title.type): this.type = { useNextArgAs = obj; this } //take object as parameter, no Title type
- type projection
class Network {
class Member(val name: String) {
val contacts = new ArrayBuffer[Network#Member] // Member of any Network object
}
...
}
- Path must be stable
• A package
• An object
• A val
• this, super, super[S], C.this, C.super, or C.super[S]
var chatter = new Network
val fred = new chatter.Member // error, chatter should be declared as val instead of var
- type alias
type Index = HashMap[String, (Int, Int)]
- structural types
def appendLines(target: { def append(str: String): Any }, //with method defined
lines: Iterable[String]) {
for (l <- lines) { target.append(l); target.append("\n") }
}
- compound type
val image = new ArrayBuffer[java.awt.Shape with java.io.Serializable]
trait ImageShape extends Shape with Serializable
Shape with Serializable { def contains(p: Point): Boolean } // with structural type
- existential type
Array[T] forSome { type T <: JComponent } // same as Array[_ <: JComponent]
Map[T, U] forSome { type T; type U <: T }
def process[M <: n.Member forSome { val n: Network }](m1: M, m2: M) = (m1, m2) //members from same network object
- self type
trait Logged {
def log(msg: String)
}
trait LoggedException extends Logged {
this: Exception => // trait only can be mixed into subclass of Exception
def log() { log(getMessage()) }
// OK to call getMessage because this is an Exception
}
- dependency injection (cake pattern)
supply component trait for each service that contains,
- any dependent components, expressed as self types
- a trait defines service interface
- an abstract val will be instantiated with an instance of service
- optionally, implementation of the service interface
trait LoggerComponent {
trait Logger { ... }
val logger: Logger
class FileLogger(file: String) extends Logger { ... }
...
}
trait AuthComponent {
this: LoggerComponent => // Gives access to logger
trait Auth { ... }
val auth: Auth
class MockAuth(file: String) extends Auth { ... }
...
}
object AppComponents extends LoggerComponent with AuthComponent { //trait order doesn't matter
val logger = new FileLogger("test.log")
val auth = new MockAuth("users.txt")
}
- abstract types (more suitable for the case that type is expected to be specified in subclass, compared to type parameter which is more suitable when type needs to be supplied at class instantiation)
trait Reader {
type Contents
def read(fileName: String): Contents // defined by abstract type Contents which is to be specified in subclass
}
class StringReader extends Reader {
type Contents = String
def read(fileName: String) = Source.fromFile(fileName, "UTF-8").mkString
}
class ImageReader extends Reader {
type Contents = BufferedImage
def read(fileName: String) = ImageIO.read(new File(fileName))
}
- family polymorphism
trait ListenerSupport {
type S <: Source
type E <: Event
type L <: Listener //abstract types
trait Event {
var source: S = _
}
trait Listener {
def occurred(e: E): Unit
}
trait Source {
this: S =>
private val listeners = new ArrayBuffer[L]
def add(l: L) { listeners += l }
def remove(l: L) { listeners -= l }
def fire(e: E) {
e.source = this
for (l <- listeners) l.occurred(e)
}
}
}
object ButtonModule extends ListenerSupport {
type S = Button
type E = ButtonEvent
type L = ButtonListener //actual types
class ButtonEvent extends Event
trait ButtonListener extends Listener // ButtonEven in method occurred
class Button extends Source {
def click() { fire(new ButtonEvent) }
}
}
object Main {
import ButtonModule._
def main(args: Array[String]) {
val b = new Button
b.add(new ButtonListener {
override def occurred(e: ButtonEvent) { println(e) }
})
b.click()
}
}
- Higher-Kinded Type
higher-kinded type,
trait Iterable[E, C[_]] {
def iterator(): Iterator[E]
def build[F](): C[F]
def map[F](f : (E) => F) : C[F]
}
real example:
trait Container[E] {//to hold values of E def +=(e: E): Unit }
trait Iterable[E, C[X] <: Container[X]] { // with type bound of Container
def iterator(): Iterator[E]
def build[F](): C[F]
def map[F](f : (E) => F) : C[F] = {
val res = build[F]()
val iter = iterator()
while (iter.hasNext) res += f(iter.next())
res
}
}
class Buffer[E : ClassTag] extends Iterable[E, Buffer] with Container[E] {//classtag for instantiating a new array
private var capacity = 10
private var length = 0
private var elems = new Array[E](capacity) // See note
def iterator() = new Iterator[E] {
private var i = 0
def hasNext = i < length
def next() = { i += 1; elems(i - 1) }
}
def build[F : ClassTag]() = new Buffer[F]
def +=(e: E) {
if (length == capacity) {
capacity = 2 * capacity
val nelems = new Array[E](capacity) // See note
for (i <- 0 until length) nelems(i) = elems(i)
elems = nelems
}
elems(length) = e
length += 1
}
}
- Implicit
implicit def int2Fraction(n: Int) = Fraction(n, 1)
val result = 3 * Fraction(4, 5) // Calls int2Fraction(3)
- enrich existing class
val contents = new File("README").read
implicit class RichFile(val from: File) extends AnyVal{
def read = Source.fromFile(from.getPath).mkString
}
- rules of implicit conversion
- type of expression differs from the expected type -> 3 * Fraction(4, 5)// Calls fraction2Double
- object accesses non-existing member -> 3.den // Calls int2Fraction
- object invokes method whose arguments don't match given arguments -> Fraction(4, 5) * 3 // Calls int2Fraction
- convert only when needed.
- no multiple conversion
- no ambiguous conversion
- implicit parameter
def quote(what: String)(implicit delims: Delimiters) =
delims.left + what + delims.right
compiler looks for value of Delimiters from (1# has precedence over 2#),
- Among all val and def of the desired type that are in scope without a prefix. (
- Implicits defined in current scope
- Explicit imports
- wildcard imports
- In the companion object of a type that is associated with the desired type. Associated types include the desired type itself and, if it is a parameterized type, its type parameters. (
- Companion objects of a type
- Implicit scope of an argument’s type (2.9.1)
- Implicit scope of type arguments (2.8.0)
- Outer objects for nested types
- implicit in subclass has precedence over one in parent class
- implicit conversion with implicit parameter
def smaller[T](a: T, b: T)(implicit order: T => Ordered[T]) // we have implicit method T => Ordered[T] defined
= if (order(a) < b) a else b
- context bound
class Pair[T : Ordering](val first: T, val second: T) {
def smaller(implicit ord: Ordering[T]) =
if (ord.compare(first, second) < 0) first else second
}
or
class Pair[T : Ordering](val first: T, val second: T) {
def smaller =
if (implicitly[Ordering[T]].compare(first, second) < 0) first else second
}
- type class
trait NumberLike[T] {
def plus(x: T, y: T): T
def divideBy(x: T, n: Int): T
}
object NumberLike {
implicit object NumberLikeDouble extends NumberLike[Double] {
def plus(x: Double, y: Double) = x + y
def divideBy(x: Double, n: Int) = x / n
}
implicit object NumberLikeBigDecimal extends NumberLike[BigDecimal] {
def plus(x: BigDecimal, y: BigDecimal) = x + y
def divideBy(x: BigDecimal, n: Int) = x / n
}
}
def average[T](x: T, y: T)(implicit ev: NumberLike[T]) =
ev.divideBy(ev.plus(x, y), 2)
or
def average[T : NumberLike](x: T, y: T) = {
val ev = implicitly[NumberLike[T]]
ev.divideBy(ev.plus(x, y), 2)
}
- evidence
def firstLast[A, C](it: C)(implicit ev: C <:< Iterable[A]) = //instance of C <:< Iterable[A] is implicitly defined.
(it.head, it.last)