本節翻譯自html
綜述:本節介紹了for推導式的使用;學習如何使用Scala特有的註解,以及如何與Java註解實現互操做。java
Scala提供了一個輕量級符號 for
表示序列推導。推導式的形式爲 for (enumerators) yield e
,其中 enumerators
是指以分號分隔的枚舉器列表。枚舉器是一個引入新變量的生成器,或者是一個過濾器。推導式求解出由枚舉器生成的每一個綁定的主體 e
,並返回這些值的序列。oracle
這裏給個例子:jvm
case class User(val name: String, val age: Int) val userBase = List(new User("Travis", 28), new User("Kelly", 33), new User("Jennifer", 44), new User("Dennis", 23)) val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30)) yield user.name // i.e. add this to a list twentySomethings.foreach(name => println(name)) // prints Travis Dennis
for
循環實際上使用 yield
語句建立了一個 List
。由於咱們說 yield user.name
是一個 List[String]
。user <- userBean
是咱們的生成器,if(user.age >= 20 && user.age < 30
是一個過濾掉20多歲用戶 (filters out users who are in their 20s) 的守衛。函數
下面是更加複雜的例子,其使用了兩個生成器。它計算在 0
和 n-1
之間的全部數值對,它們的和等於給定的值 v
:學習
def foo(n: Int, v: Int) = for (i <- 0 until n; j <- i until n if i + j == v) yield (i, j) foo(10, 10) foreach { case (i, j) => print(s"($i, $j) ") // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) }
這裏 n == 10
、v == 10
。在第一次迭代過程當中 i == 0
、j == 0
,因此 i + j != v
,所以沒有東西的產出。在 i
遞增到1以前,j
會遞增9次。若是沒有 if
守衛,該方法只會簡單地打印出如下內容:this
(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 1) ...
注意,推導式並不侷限於列表。每個支持 withFilter
、map
和 flapMap
(用適當的類型)的數據類型,均可以使用序列推導式。編碼
你能夠在推導式中忽略 yield
。在這種狀況下,推導式將返回 Unit
。若是你須要執行反作用,這可能頗有用。下面這個程序的結果至關於上面的,但沒有使用 yield
:scala
def foo(n: Int, v: Int) = for (i <- 0 until n; j <- i until n if i + j == v) print(s"($i, $j)") foo(10, 10)
註解將元信息與定義關聯起來。例如,若是在方法以前有 @deprecated
註解,則方法被使用會致使編譯器打印警告。翻譯
object DeprecationDemo extends App { @deprecated def hello = "hola" hello }
這段程序將編譯,可是編譯器會打印警告:「there was one deprecation warning」。
註解子句適用於它後面的第一個定義或聲明。多個註解子句可能在定義和聲明以前出現。這些子句的順序可有可無。
若是條件不知足,某些註解實際上會致使編譯失敗。例如,@tailrec
的註解確保了一個方法是尾遞歸的。尾遞歸能夠保持內存需求不變。下面展現瞭如何在計算階乘的方法中使用它:
import scala.annotation.tailrec def factorial(x: Int): Int = { @tailrec def factorialHelper(x: Int, accumulator: Int): Int = { if (x == 1) accumulator else factorialHelper(x - 1, accumulator * x) } factorialHelper(x, 1) }
factorialHelper
方法有一個 @tailrec
註解,它確保了該方法確實是尾遞歸的。若是咱們將 factorialHelper
的實現更改成如下內容,那麼它就會失敗:
import scala.annotation.tailrec def factorial(x: Int): Int = { @tailrec def factorialHelper(x: Int): Int = { if (x == 1) 1 else x * factorialHelper(x - 1) } factorialHelper(x) }
咱們會獲得一個「Recursive call not in tail position」的消息。
一些註解如 @inline
會影響生成的代碼(也就是說,若是你沒有使用註解的話,你的jar文件可能有不一樣的字節)。內聯意味着將代碼插入到調用地點的方法的主體中。結果字節碼更長,但有但願運行得更快。使用 @inline
的註解並不能確保一個方法是內聯的,可是當且僅當知足一些關於生成代碼大小的啓發式規則時,纔會致使編譯器作到這一點。
在編寫與 Java 交互的 Scala 代碼時,註解語法有一些不一樣之處。注意:確保使用了 -target:jvm-1.8
選項和 Java 註解。
Java 以註解的形式擁有用戶定義的元數據。註釋的一個關鍵特徵是它們依賴於指定 name-value
對來初始化它們的元素。例如,若是咱們須要一個註釋來跟蹤某個類的源頭,咱們能夠將它定義爲
@interface Source { public String URL(); public String mail(); }
而後把它應用到下面
@Source(URL = "http://coders.com/", mail = "support@coders.com") public class MyClass extends HisClass ...
Scala 中的註解應用程序看起來就像是構造函數調用,用於實例化一個 Java 註釋,它必須使用命名參數:
@Source(URL = "http://coders.com/", mail = "support@coders.com") class MyScalaClass ...
若是註解只包含一個元素(沒有默認值),那麼這個語法就很麻煩。所以,按照約定,若是名稱被指定爲 value
,則能夠在Java中使用相似於構造器的語法:
@interface SourceURL { public String value(); public String mail() default ""; }
而後把它應用到下面
@SourceURL("http://coders.com/") public class MyClass extends HisClass ...
在這種狀況下,Scala 提供了一樣的可能性
@SourceURL("http://coders.com/") class MyScalaClass ...
mail
元素是用缺省值指定的,所以咱們不須要顯式地爲它提供一個值。可是,若是咱們這樣作了,咱們就不能在Java中混合使用兩種風格:
@SourceURL(value = "http://coders.com/", mail = "support@coders.com") public class MyClass extends HisClass ...
Scala在這方面提供了更多的靈活性
@SourceURL("http://coders.com/", mail = "support@coders.com") class MyScalaClass ...