15 注解

本章要点

  • 本章需要Java、Spring相关的基础
  • 注解让可以在程序的各项条目中添加信息,这些信息可以被编译器或外部工具处理
  • 可以为类、方法、字段、局部变量、参数、表达式、类型参数以及各种类型定义添加注解
  • 表达式和类型的注解跟在被注解的条目之后
  • 注解的形式
  • 生成等效的java修饰符@volatile,@transient,@strictfp,@native
  • @throws生成与java兼容的throws规格说明
  • @tailrec校验某个递归函数使用了尾递归优化
  • assert函数使用了@elidable注解
  • @deprecated标记已过时的特性

什么是注解

  • 注解是插入代码以便有工具可以对它们进行处理的标签,工具可以在代码级别运作,也可以处理被编译器加入了注解信息的类文件
  • 注解在Java中广泛使用
  • 注解的语法和Java 一样
  • 可以对Scala类使用Java注解,也可以使用Scala独有的注解
  • Java注解不影响编译器如何将源码翻译成字节码,仅仅是在字节码中添加数据,以便外部工具可以利用他们,在Scala中,注解可以影响编译过程

什么可以被注解

  • 类、方法、字段、局部变量、参数等
  • 可以同时添加多个注解,不区分先后顺序
  • maven依赖
    <!-- https://mvnrepository.com/artifact/javax.persistence/javax.persistence-api -->
    <dependency>
        <groupId>javax.persistence</groupId>
        <artifactId>javax.persistence-api</artifactId>
        <version>2.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.testng/testng -->
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>6.14.3</version>

    </dependency>
import javax.persistence.{Entity, Id}
import org.testng.annotations.Test
import scala.beans.BeanProperty


@Entity abstract class Credentials 
@Test def testSomeFeature() {}
@BeanProperty
@Id var username = ""
//Error:(16, 20) trait NotNull is abstract; cannot be instantiated
def doSomething(@NotNull mesage: String) {}
  • 在给主构造器添加注解时,需要将注解放置在构造器之前,并加上一对圆括号(如果注解不带参数的话)
class Credentials1 @Inject()(var username:String,var passwd:String)
  • 为表达式添加注解,在表达式后加冒号,然后注解
 (myMap.get(key):@unchecked) match {}
  • 为类型参数添加注解
 class MyContainter[@specialized T]
  • 针对实际类型的注解应放置在类型名称之后
def country:String @Localized

注解参数

  • Java注解可以有带名参数
  • 如果参数名为value,则该名称可以省去
  • 如果注解不带参数,则圆括号可以省去
  • 大多数注解参数都有默认值
@Test(timeout = 100,expected = classOf[IOException])
@Named("creds") var cred:Credentials = _
@Entity class Credentials
  • Java注解的参数类型只能是,数值型字面量、字符串、类字面量、Java枚举、其他注解、上述类型的数组
  • Scala注解的参数可以是任何类型,但是少数注解利用了这个额外的灵活性

注解实现

  • 明白自己的注解类是怎么实现的
  • 注解必须扩展Annotation特质,例如unchecked注解
  • 类型注解必须扩展自TypeAnnotation特质 ,例如Localized注解
  • Java注解需要使用Java编写
class unchecked extends annotation.Annotation
class Localized extends StaticAnnotation with TypeConstraint
  • 注解的作用是描述那些注解的表达式、变量、字段、方法、类、类型,
  • Scala的字段定义可能会引出多个Java特性,它们都有可能被添加注解
  • 默认情况下,构造器参数注解仅应用到参数自身,而字段注解只能应用到字段,元注解(@param、@field、@getter、@setter、@beanGetter、@beanSetter)将使注解敷在别处

针对Java特性的注解

  • 与Java互操作的注解

Java修饰符

  • 对于不常用的Java特性,scala使用注解而不是修饰符关键字
  • @vilatile注解将字段标记为易失的,易失的字段可以被多个线程同时更新
  • @transient注解将字段标记为瞬态的,瞬态的字段不会被序列化,对于临时保存的缓存数据
  • @strictfp注解对应Java中的strictfp修饰符,使用IEEE的double值进行浮点计算,而不是使用80位扩展精度(Intel处理器的默认实现),使代码移植性更高
  • @native标记在C或C++中实现的方法,对于Java的native修饰符

标记接口

  • @cloneable和@remote标记可被克隆的和远程的对象
  • 对于可序列化的类,使用@SerialVersionUID注解指定序列化的版本

受检异常

  • 如果从Java调用Scala的方法,其签名包含那些可能被抛出的受检异常,用@throws生成正确的签名
  • 如果没有@throws注解,java代码将不能捕获该异常

变长参数

  • @varargs注解使得可以从java调用scala的带有变长参数的方法
    def process(arg:String*){}  //会把变长参数翻译成序列,在java 中使用很费劲
    def process(arg:Seq[String]){}
    //加注解的
    @varargs def process1(args:String*){}
    void process1(String... args) //java桥接方法,将args数组包装在Seq中然后调用实际的scala方法
   

JavaBeans

  • @BeanProperty注解,除了scala版的getter和setter还会生成Java版的getter和setter
  • @BooleanBeanProperty对类型为Boolean的字段生成带有is前缀的getter方法

用于优化的注解

  • 有些注解可以控制编译器优化

尾递归

  • 递归调用有时能被转化成循环,节约栈空间
  • sum的最后一步是加法不是递归调用,无法被优化
  • sum2是递归调用同一个方法,编译器会自动对sum2应用尾递归优化
  • sum2(1 to 1000000)会报错 栈溢出错误,sum2(1 to 1000000,0)能够正常得到结果
def sum(xs:Seq[Int]):BigInt={
  if (xs.isEmpty) 0 else xs.head + sum(xs.tail)
}

def sum2(xs:Seq[Int],partial:BigInt):BigInt={
  if (xs.isEmpty) partial else sum2(xs.tail,xs.head + partial)
}
  • 使用@tailrec注解,如果编译器无法优化,就会报错
  • 例如将sum2放在类中,可以声明为final或private或者放在object中
import scala.annotation.tailrec
class A{
  @tailrec def sum2(xs:Seq[Int],partial:BigInt):BigInt={
    if (xs.isEmpty) partial else sum2(xs.tail,xs.head + partial)
  }
}
报错error: could not optimize @tailrec annotated method sum2: it is neither private nor final so can be overridden

跳转表生成与内联

  • C++或java中的,switch语句通常可以编译成跳转表,比if/else更高效
  • scala 也会对匹配语句生成跳转表,@switch注解可以检查match语句是不是被编译成了跳转表
import scala.annotation.switch
var n :Int = 0
(n: @switch)match{
  case 0 => "Zero"
  case 1 => "one"
  case _ => "?"
}
  • 内联:将方法调用语句替换为被调用的方法体
  • @inline建议编译器内联,@noinline告诉编译器不要内联

可省略方法

  • @elidable 那些在生产代码中移除的方法

基本类型特殊化

用于错误和警告的注解

  • @deprecated注解,编译器遇到时会发出警告信息,两个选填参数,message和since
  • @deprecatedName 可以被应用到参数上,给出一个该参数之前使用过的名称,例如@deprecatedName('sz),符号——以单引号开头的名称,符号表示程序中某个项目的名称
  • @deprecatedInheritance和@deprecatedOverriding针对从某个类继承或重写方法的情况生成过期警告,对应的类或方法在以后的版本可能被改成final
  • @implicitNotFound,@implicitAmbiguous某个隐式参数不存在或不明确的时候生成有意义的错误提示
  • @unchecked 匹配不完整时取消警告信息
  • @uncheckedVariance取消与型变相关的错误提示
发布了57 篇原创文章 · 获赞 73 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_40450867/article/details/103790687
15