scala notes (6) - Annotation, Future and Type Parameter

- Annotation

class MyContainer[@specialized T]
def country: String @Localized
@Test(timeout = 0, expected = classOf[org.junit.Test.None])
def testSomeFeature() { ... }

  • Java annotation can be mixed with Scala annotation. scala just put them in code for external tools to process
  • annotation implementaton
class unchecked extends annotation.Annotation

class Localized extends StaticAnnotation with TypeConstraint
• The constructor parameter @param
• The private instance field @field
• The accessor method username @getter
• The mutator method username_= @setter
• The bean accessor getUsername @beanGetter
• The bean mutator setUsername @beanSetter

  • scala annotations for Java features (@volative, @transient, @strictfp (IEEE double values), @native, @cloneable, @SerialVersionUID, @BeanProperty)

@throws to throw checked exception for call from Java

class Book {
    @throws(classOf[IOException]) def read(filename: String) { ... }
    ...
}

@varargs to generate Java bridge method for "args: String*" 

def process(args: String*) // ("x", "y", "z") or (seq : _*)
def process(args: Seq[String]) // scala without the annotation

@varargs def process(args: String*)
void process(String... args) // Java bridge method

@tailrec to force scala compiler to turn recursive call to loop to save stack space. error will be reported if compiler cannot turn it. the method needs to be non-overridden (private or final)

@tailrec def sum2(xs: Seq[Int], partial: BigInt): BigInt =
if (xs.isEmpty) partial else sum2(xs.tail, xs.head + partial) //last statement is same as method

@elidable to remove method from production. the method should not return any value because the elided method is replaced with Unit object. classcastexception could happen if you use the return value.

@elidable(500) def dump(props: Map[String, String]) { ... }

@specialized to not wrap primitives by adding more methods for primitives

def allDifferent[@specialized T](x: T, y: T, z: T) = ...

def allDifferent(x: Int, y: Int, z: Int) = ...

def allDifferent[@specialized(Long, Double) T](x: T, y: T, z: T) = ...

def allDifferent(x: Long, y: Long, z: Long) = ...

def allDifferent(x: Double, y: Double, z: Double) = ...

  • annotation for warning and error

@deprecated(message="xxx") @deprecatedName('sz) @deprecatedInheritance @deprecatedOverriding

@unchecked to suppress a match not exhaustive warning

(lst: @unchecked) match { //Nil is needed
    case head :: tail => ...
}

@uncheckedVariance to enbale variance in Java generics

e.g. If Student is a subtype of Person, then a Comparator[Person] can be used when a Comparator[Student] is required.

trait Comparator[-T] extends
java.lang.Comparator[T @uncheckedVariance] // for Java generics


- Future

  • Future result is either Success or Failure. it's equivalent to CompletionStage in Java 8.
val f = Future {
    Thread.sleep(10000)
    42
} // f could be Future(<not completed>), Future(Success(42))
val f2 = Future {
    if (LocalTime.now.getHour > 12)
        throw new Exception("too late")
    42
}//Future(Failure(java.lang.Exception: too late))

val t = f.value return Option[Try[T]] object; It's None when it's not completed

t match {
case Success(v) => println(s"The answer is $v") //v is type of T
case Failure(ex) => println(ex.getMessage)
}

val rst = Try(str.toInt) // to instantiate Try object

f.onComplete {case Success(v) => ...; case Failure(ex) =>...} //can combine anothe future object in Success statement

  • combine tow futures

Future[T] is collection of (hopefully, eventually) one value.

val future1 = Future { getData1() }
val future2 = Future { getData2() }

val combined = f1.flatMap(n1 => f2.map(n2 => n1 + n2)) // 
val combined = for (n1 <- future1; n2 <- future2) yield n1 + n2 // traslated to map and flatmap
val combined = async { await(future1) + await(future2) } // or Async Library
  • delay future run by defining it as function, "def future1 = Future {getData1()}"
  • methods in Future (foreach, recover, fallBackTo...)
  • methods in Future Object
val futures = parts.map(p => Future { compute result in p }) // Set[Future[T]]
val result = Future.sequence(futures); // Future[Set[T]]
or 
val result = Future.traverse(parts)(p => Future { compute result in p }) //Future[Set[T]]

firstCompletedOf, reduceLeft, find.

to generate simple futures, 

Future.successful(r), Future.failed(e), Future.fromTry(t),  Future.unit,  Future.never

  • Promise
def computeAnswer(arg: String) = {//same for consumer, but more for producer
    val p = Promise[Int]()
    Future {
        val n = workHard(arg)
        p.success(n)
        workOnSomethingElse() // more work of producer
    }
    p.future //immediately returned after Future startup. it's promise's associated future
}
val p1 = Promise[Int]()
val p2 = Promise[Int]()
Future { // one producer more promises
    val n1 = getData1()
    p1.success(n1)
    val n2 = getData2()
    p2.success(n2)
}
val p = Promise[Int]() // multiper workers for same promise
Future {
    var n = workHard(arg)
    p.trySuccess(n)
} Future {
    var n = workSmart(arg)
    p.trySuccess(n)
}
  • ExecutionContext
val f = Future {
    val url = ...
    blocking { //to notify context about to blocking
        val contents = Source.fromURL(url).mkString
        ...
    }
}

or more suitable thread pool for different tasks

val pool = Executors.newCachedThreadPool()
implicit val ec = ExecutionContext.fromExecutor(pool) // put it in scope

- Type Parameter

  • generic type
class Pair[T, S](val first: T, val second: S)

val p2 = new Pair[Any, Any](42, "String")
def getMiddle[T](a: Array[T]) = a(a.length / 2)

val f = getMiddle[String] _ // The function, saved in f for specific type string
  • Bound for type parameter
class Pair[T <: Comparable[T]](val first: T, val second: T) { //<:
    def smaller = if (first.compareTo(second) < 0) first else second
}
class Pair[T](val first: T, val second: T) {
    def replaceFirst(newFirst: T) = new Pair[T](newFirst, second)//could be better to apply method to both Pair[Person] and Pair[Student]
}
def replaceFirst[R >: T](newFirst: R) = new Pair[R](newFirst, second) // return Pair[R]. R is super type of T
  • implicit conversion
class Pair[T](val first: T, val second: T)(implicit ev: T => Comparable[T]) { // T can be converted to Comparable[T]
    def smaller = if (first.compareTo(second) < 0) first else second
    ...
}
  • Context Bound
class Pair[T : Ordering](val first: T, val second: T) { // there is a implicit value of Ordering[T] in scope
    def smaller(implicit ord: Ordering[T]) =
        if (ord.compare(first, second) < 0) first else second
}
  • Classtag to instantiate array of type T
import scala.reflect._
    def makePair[T : ClassTag](first: T, second: T) = {//object of ClassTag[T] needs to be in scope
    val r = new Array[T](2); r(0) = first; r(1) = second; r
}
  • multiple bounds
T >: Lower <: Upper
T <: Comparable[T] with Serializable with Cloneable
T : Ordering : ClassTag
  • type constraint
def firstLast[A, C](it: C)(implicit ev: C <:< Iterable[A]) =
    (it.head, it.last)
  • variance

  1. covariant type cannot be in contra-variant position (set method) -> solution: use lower-bound in set method 
def m(U >: T)(x: U) = new Instance(x)

  1. and vice-versa. contra-variant type cannot be in covariant position (get method)

exception is object private (private[this]) variable is not checked for variance type and position.

Java array is covariant. But scala array is not

  1. covariant
class Pair[+T](val first: T, val second: T)

Student <: Person
Pair[Student] <: Pair[Person]
  1. contravariant
trait Friend[-T] {
    def befriend(someone: T)
}

Student <: Person
Friend[Person] <: Friend[Student]
  1. both variances in Function1[-A, +R]
def friends(students: Array[Student], find: Function1[Student, Person]) =
// You can write the second parameter as find: Student => Person
for (s <- students) yield find(s)
def findStudent(p: Person) : Student // this function can be applied to friends method
class Publication(val title: String)
class Book(title: String) extends Publication(title)
object Library {
    val books: Set[Book] =
    Set(
        new Book("Programming in Scala"),
        new Book("Walden")
    )
    def printBookList(info: Book => AnyRef) {
        for (book <books)
            println(info(book))
    }
}
object Customer extends Application {
    def getTitle(p: Publication): String = p.title//function can be safely applied to function1[-book, +AnyRef]
    Library.printBookList(getTitle)
}

  • wildcards
def makeFriends(p: Pair[_ <: Person]) //OK to call with a Pair[Student]


















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章