【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 ...
相关文章
相关标签/搜索