注解深入浅出之自定义注解(一)

更多Android架构进阶视频学习请点击:https://space.bilibili.com/474380680
本篇文章将从以下几个内容来阐述注解:

  • [注解]
  • [元注解]
  • [自定义注解]
  • [注解参数默认值]

一、注解(ANNOTATIONS)

注解将元信息(meta-information)与定义相关联。例如:方法前的注解 @deprecated 会造成编译器在该方法被调用时打印警告信息。

object DeprecationDemo extends App {
  @deprecated
  def hello = "hola"

  hello  
}

这个程序可以编译但是编译器会打印一句警告:“there was one deprecation warning”

注解子句适用于其后面的第一次定义或声明。一个以上的注解子句可以出现在定义和声明之前。这些子句的顺序并不重要。

1.1注解确保编码的正确性

如果有些条件不符合,某些注解可能会导致编译失败。例如,注解 @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)
}
19956127-d1a8e33d9d568799
这里写图片描述

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)
}
19956127-869f1bb562a8bdc5
这里写图片描述

我们将获得提示信息:"Recursive call not in tail position"

1.2 影响代码生成的注释

一些类似 @inline 的注释会影响生成的代码(即你的 jar 文件可能相比不使用注解会有不同的字节数)。内联(Inlining)指在调用点的方法中插入代码。生成的字节码会更长,但希望运行的会更快点。使用注解 @inline 并不能确保方法将被内联,但是当且仅当满足生成代码块的一些启发性算法时,它将会触发编译器去进行内联操作。

1.3 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 ...

二、元注解

java内置的注解有Override, Deprecated, SuppressWarnings等, 作用相信大家都知道.

现在查看Override注解的源码

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

发现Override注解上面有两个注解, 这就是元注解. 元注解就是用来定义注解的注解.其作用就是定义注解的作用范围, 使用在什么元素上等等, 下面来详细介绍.

元注解共有四种@Retention, @Target, @Inherited, @Documented

  • @Retention 保留的范围,默认值为CLASS. 可选值有三种

    • SOURCE, 只在源码中可用
    • CLASS, 在源码和字节码中可用
    • RUNTIME, 在源码,字节码,运行时均可用
  • @Target 可以用来修饰哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER等,未标注则表示可修饰所有

  • @Inherited 是否可以被继承,默认为false

  • @Documented 是否会保存到 Javadoc 文档中

其中, @Retention是定义保留策略, 直接决定了我们用何种方式解析. SOUCE级别的注解是用来标记的, 比如Override, SuppressWarnings. 我们真正使用的类型是CLASS(编译时)和RUNTIME(运行时)

三、自定义注解

举个例子, 结合例子讲解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TestAnnotation {
    String value();
    String[] value2() default "value2";
}

元注解的的意义参考上面的讲解, 不再重复, 这里看注解值的写法:

类型 参数名() default 默认值;

其中默认值是可选的, 可以定义, 也可以不定义.

四、 默认参数值(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
    } 
}
19956127-e54da6be4ee9ff4f

更多Android架构进阶视频免费学习请点击:https://space.bilibili.com/474380680
参考:https://www.jianshu.com/p/a13c6326671d
https://blog.csdn.net/smile_from_2015/article/details/80345149

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值