注解就是标签。
标签是用来标记某些代码需要特殊处理的。
处理的手段可以在代码运行时操作,也可以在编译期操作。
1 什么可以被注解
1) 可以为类,方法,字段局部变量,参数,表达式,类型参数以及各种类型定义添加注解
@Entity class Student |
2) 构造器注解,需要在主构造器之前,类名之后,且需要加括号,如果注解有参数,则写在注解括号里
class Student @Inject() (var username: String, var password: String) |
3) 为表达式添加注解,在表达式后添加冒号
(map1.get(key): @unchecked) match {...} |
4) 泛型添加注解
class Student[@specialized T] |
5) 实际类型添加注解
String @cps[Unit] |
2 注解参数
Java注解可以有带名参数:
@Test(timeout = 100, expected = classOf[IOException]) |
Java 注解的参数类型只能是:
数值型的字面量
字符串
类字面量
Java枚举
其他注解
上述类型的数组(但不能是数组的数组)
Scala注解可以是任何类型,但只有少数几个Scala注解利用了这个增加的灵活性。
3 注解实现
你可以实现自己的注解,但是更多的是使用Scala和Java提供的注解。
注解必须扩展Annotation特质:
class unchecked extends annotation.Annotation |
4 针对Java的注解
1) Java修饰符:对于那些不是很常用的Java特性,Scala使用注解,而不是修饰符关键字。
@volatile var done = false // JVM中将成为volatile的字段 |
2) 标记接口:Scala用注解@cloneable和@remote 而不是 Cloneable和Java.rmi.Remote“标记接口”来标记可被克隆的对象和远程的对象。
@cloneable class Employee |
3) 受检异常:和Scala不同,Java编译器会跟踪受检异常。如果你从Java代码中调用Scala的方法,其签名应包含那些可能被抛出的受检异常。用@throws注解来生成正确的签名。
class Book { |
即:Java编译期需要在编译时就知道read方法可以抛IOException异常,否则Java会拒绝捕获该异常。
5 由于优化的注解
尾递归的优化
啥玩是尾递归?
尾递归:
def story(): Unit = {从前有座山,山上有座庙,庙里有个老和尚,一天老和尚对小和尚讲故事:story()} |
尖叫提示:进入下一个函数不再需要上一个函数的环境了,得出结果以后直接返回。
非尾递归:
def story(): Unit = {从前有座山,山上有座庙,庙里有个老和尚,一天老和尚对小和尚讲故事:story(),小和尚听了,找了块豆腐撞死了} |
尖叫提示:下一个函数结束以后此函数还有后续,所以必须保存本身的环境以供处理返回值。
递归调用有时候能被转化成循环,这样能节约栈空间:
object Util { |
上面的sum方法无法被优化,因为计算过程中最后一步是加法,而不是递归调用。调整后的代码:
def sum2(xs: Seq[Int], partial: BigInt): BigInt = { |
Scala编译器会自动对sum2应用“尾递归”优化。如果你调用sum(1 to 1000000) 将会发生一个栈溢出错误。不过sum2(1 to 1000000, 0) 将会得到正确的结果。
尽管Scala编译器会尝试使用尾递归优化,但有时候某些不太明显的原因会造成它无法这样做。如果你想编译器无法进行优化时报错,则应该给你的方法加上@tailrec注解。
尖叫提示:对于消除递归,一个更加通用的机制叫做“蹦床”。蹦床的实现会将执行一个循环,不停的调用函数。每个函数都返回下一个将被调用的函数。尾递归在这里是一个特例,每个函数都返回它自己。Scala有一个名为TailCalls的工具对象,帮助我们轻松实现蹦床:
import scala.util.control.TailCalls._ |