【Scala之旅】控制結構和註解

本節翻譯自html

綜述:本節介紹了for推導式的使用;學習如何使用Scala特有的註解,以及如何與Java註解實現互操做。java

for推導式

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) 的守衛。函數

下面是更加複雜的例子,其使用了兩個生成器。它計算在 0n-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 == 10v == 10。在第一次迭代過程當中 i == 0j == 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) ...

注意,推導式並不侷限於列表。每個支持 withFiltermapflapMap(用適當的類型)的數據類型,均可以使用序列推導式。編碼

你能夠在推導式中忽略 yield。在這種狀況下,推導式將返回 Unit。若是你須要執行反作用,這可能頗有用。下面這個程序的結果至關於上面的,但沒有使用 yieldscala

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註解

在編寫與 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 ...
相關文章
相關標籤/搜索