注解(ANNOTATIONS)
注解将元信息(meta-information)与定义相关联。例如:方法前的注解 @deprecated
会造成编译器在该方法被调用时打印警告信息。
object DeprecationDemo extends App {
@deprecated
def hello = "hola"
hello
}
这个程序可以编译但是编译器会打印一句警告:“there was one deprecation warning”
。
注解子句适用于其后面的第一次定义或声明。一个以上的注解子句可以出现在定义和声明之前。这些子句的顺序并不重要。
注解确保编码的正确性
如果有些条件不符合,某些注解可能会导致编译失败。例如,注解 @tailrec
确保方法是尾递归的(tail-recursive)。尾递归(Tail-recursion)可以确保内存需求保持不变。下面例子是如何在计算阶乘的方法中使用它:
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
用于确保方法的确是尾递归的(tail-recursive)。如果我们按照以下的方式改变 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 文件可能相比不使用注解会有不同的字节数)。内联(Inlining)指在调用点的方法中插入代码。生成的字节码会更长,但希望运行的会更快点。使用注解 @inline
并不能确保方法将被内联,但是当且仅当满足生成代码块的一些启发性算法时,它将会触发编译器去进行内联操作。
Java 注解
在编译与 Java 互操作的 Scala 代码时,注解语法有一些不同之处值得注意:确保你在使用 Java 注解时使用 -target:jvm-1.8
选项。
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 ...
默认参数值(DEFAULT PARAMETER VALUES)
Scala 提供了参数默认值功能,可以允许调用方省略那这些参数。
def log(message: String, level: String = "INFO") = println(s"$level: $message")
log("System starting") // prints INFO: System starting
log("User not found", "WARNING") // prints WARNING: User not found
该参数 level
有一个默认值,因此它是可选的。在最后一行,参数 "WARNING"
覆盖了默认参数 "INFO"
。在 Java 中可能执行重载方法的地方,你可以使用可选参数的方法来获得相同效果。但是,如果调用者省略一个参数,任何接下来的参数都必须被命名。
class Point(val x: Double = 0, val y: Double = 0)
val point1 = new Point(y = 1)
在这里我们必须要明确 y = 1
。
注意从 Java 代码里调用时, Scala 中的默认参数不是可选的。
// Point.scala
class Point(val x: Double = 0, val y: Double = 0)
// Main.java
public class Main {
public static void main(String[] args) {
Point point = new Point(1); // does not compile
}
}