返回介绍

数学基础

统计学习

深度学习

工具

Scala

三、注解

发布于 2023-07-17 23:38:22 字数 6115 浏览 0 评论 0 收藏 0

  1. 注解是添加到程序源代码中的结构化信息。

    • 和注释一样,注解可以出现在程序中的任何位置,附加到任意变量、方法、表达式或其它程序元素上。
    • 和注释不同,注解有结构,因此更容易机器处理。

    这里我们主要介绍如何使用注解,而不是如何编写新的注解。编写新的注解不是我们关注的重点。因为使用注解要比编写新注解常用的多。

  2. 一个典型的注解示例:

    
    
    xxxxxxxxxx
    @deprecated def f() = ...

    注解可以用于各种声明、定义上,包括 val, var, def, class, object, trait, type 。注解对于跟在它后面的整个声明或定义有效。

    注解也可以用于表达式,做法是在表达式后面写一个冒号 : 再写注解。从语法角度来看,注解像是被用在了类型上:

    
    
    xxxxxxxxxx
    (e: @unchecked) match{ //... }
  3. 目前为止所给的注解都是 @ 加上注解类名的方式,不过注解也有更丰富的一般格式:

    
    
    xxxxxxxxxx
    @annot(exp1,exp2,...)

    其中:

    • annot 是注解类名,所有的注解都必须包含。

    • exp 部分是给注解的入参。对于 @deprecated 这样的注解而言,它们并不需要入参,因此通常可以省略圆括号。但是你也可以这样写:@deprecated()

      对于确实需要入参的注解,需要将入参写到圆括号中,例如 @serial(1234)

    你提供给注解的入参的形式取决于特定的注解类。大多数注解处理器只允许你提供直接常量,如 123 或者 "hello"。 不过编译器本身(对于注解而言)是支持任意表达式的,只要它们能够通过类型检查。如:

    
    
    xxxxxxxxxx
    @cool val normal = "hello" @coolerThan(normal) val fonzy = "world"

    scala 在内部将注解表示为仅仅是对某个注解类的构造方法的调用(想象下如果将 @ 替换为 new)。这意味着编译器可以很自然地支持注解的带名字的参数和默认参数,因为 scala 已经支持方法和构造方法调用的带名字参数和默认参数。

    不能直接把注解当做另一个注解的入参,因为注解并不是合法的表达式。在这种情况下,必须用 new 或者 @

    
    
    xxxxxxxxxx
    import annotation._ class strategy(arg: Annotation) extends Annotation class delayed extends Annotation @strategy(new delayed) def f()=...
  4. scala 包含了若干标准注解,它们是为一些非常常用的功能服务的,因此就被放在了语言规范中。不过还没有达到足够基础的程度,因此并没有自己的语法。

    • deprecated:可以将方法、类标记为 deprecated,这样任何人调用了这个方法或类都会得到一个 deprecation 警告。当经过一段时间之后,就可以假定使用方不再访问这个方法或类,因此可以安全地移除这个方法或类。

      可以简单地在方法/类之前写上 @deprecated。也可以提供一个字符串作为入参,此时这个字符串将在编译时随警告一起提示出来。通常这个字符串可以告诉大家解释该方法/类已经过时,应该如何升级。

    • volatile:可以将变量标记为 volatile,从而告诉编译器这个变量会被多个线程使用。这样的变量实现的效果使得读写更慢,但是从多个线程访问时的行为更可预期。

      事实上 scala 鼓励使用不可变的对象以及函数式编程,因此比较少地使用共享的可变状态。因此 @volatilescala 中应用较少。

    • 序列化:序列化框架可以帮助我们将对象转化为字节流,或者将字节流还原为对象。当你希望将对象保存到磁盘或者将对象通过网络发送时很有帮助。

      scala 并没有自己的序列化框架,而是使用底层平台提供的框架。scala 能做的是提供三个可被不同框架使用的注解。针对 Java 平台的 scala 编译器会以 java 的方式来解释这些注解。

      • serializable:该注解用于表示某个类是否支持序列化。大多数类都是可序列化的,但是有些类不支持,如套接字或 GUI 窗口的句柄就不能被序列化。

        默认情况下,系统不会认为类是可以序列化的,因此你需要给你认为可以序列化的类添加 @serializable 注解。

      • SerialVersionUID(1234):该注解用于表示版本可变的序列化。有些类,随着时间推移它可能发生变化(比如一个新的修改)。因此可以通过添加 SerialVersionUID(1234) 这样的注解来对某个类的当前版本带上一个序列号,其中 1234 可以替换为你想要的序列号。

        序列化框架将会把这个序列号保存在生成的字节流中。当稍后你从字节流中反序列化出对象时,框架可以检查对应类的当前版本是否和字节流中的版本一致。框架会自动拒绝载入老版本的对象。

      • transient:该注解用于标记那些完全不应该被序列化的字段。

        如果你将某个字段标记为 @transient,那么就算包含该字段的对象被序列化了,序列化框架也不会保存该字段。当从字节流重新载入对象时,注解为 @transient 的这个字段将会被恢复成对应类型的默认值。

    • scala.reflect.BeanProperty :该注解会为字段自动生成 getset 方法。

      实际上 scala 代码通常不需要显式给出字段的 getset 方法,因为 scala 混合了字段访问和方法调用的语法。不过有一些特定的框架可能希望你提供 getset 方法。

      此时可以使用 @scala.reflect.BeanProperty 注解,该注解作用在字段上可以为字段自动生成 getset 方法。如果该字段名字叫 xyz,则get 方法自动命名为 getXyzset 方法自动命名为 setXyz

      注意:生成的 getset 方法仅在编译后可用。因此,你不能在编写代码的时候调用这些 getset 方法。但在实际应用中这不是问题,因为在 scala 中你可以直接访问这些字段。

    • tailrec :该注解用于对尾递归方法进行尾递归优化。

      如果尾递归优化因为某些原因无法执行优化,那么你将会得到一个警告,并告诉你为什么无法优化。

    • unchecked:该注解用在处理模式匹配的时候,告诉编译器不要担心 match 表达式可能看上去漏了某些 case

    • native:该注解告诉编译器某个方法的实现是由运行时而非scala 代码提供的。编译器会在输出中开启合适的标记,将由开发者利用诸如 java 本地接口 JNI 的机制来提供实现。

      当使用 @native 注解时,必须提供方法体,不过这个方法体并不会被包含在输出当中。如,以下是声明一个由运行时提供的 f 方法:

      
      
      xxxxxxxxxx
      @native def f() = {}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文