当前位置:嗨网首页>书籍在线阅读

04-JSR-305支持

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

15.1.3 JSR-305支持

众所周知,IntelliJ IDEA等IDE工具已经实现了部分代码检查功能,诸如FindBugs、Checkstyle和PMD这样的静态分析工具在Java开发中也得到了广泛应用。虽然这些工具都很强大,但是有一些问题是它们都很难解决的,比如对null值的判断,或者数值为负的情况。

为了解决这些问题,有些静态分析工具试图使用注解来定义相关细节,比如FindBugs和IntelliJ都定义了自己的注解,以标识方法返回为null的情况。虽然注解的使用方法有细微不同,但它们都遵循JSR-305标准。而Kotlin也在最近的版本中添加了对SR-305标准的支持。

在Kotlin中,使用定义的@Nonnull注解可以表示Java类型的可空性,使用@Nonnull注解有以下几种情况:如果@Nonnull(when = ...)值为When.ALWAYS,那么该注解类型会被视为非空;当值为When.MAYBE或When.NEVER时表示可空类型;当值为When.UNKNOWN时,标识强制类型为平台类型。

Kotlin编译器可以从注解库中读取JSR-305注解,而不需要指明该注解的完整路径。从Kotlin 1.1.5版本开始,还可以使用自定义可空限定符。

1.类型限定符别称

如果一个注解类型同时标注有@TypeQualifierNickname与JSR-305@Nonnull(或者其他别称,如@CheckForNull),那么该注解类型自身可以精确地检索可空性,而且具有与该可空性注解相同的含义。代码如下。

@TypeQualifierNickname
@Nonnull(when = When.ALWAYS)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyNonnull {
}
@TypeQualifierNickname
@CheckForNull 
@Retention(RetentionPolicy.RUNTIME)
public @interface MyNullable {
}
interface A {
    @MyNullable String foo(@MyNonnull String x); 
    //在Kotlin(严格模式)中:`fun foo(x: String): String?`
    String bar(List<@MyNonnull String> x);       
    //在Kotlin(严格模式)中:`fun bar(x: List<String>!): String!`
}

2.类型限定符默认值

如果@TypeQualifierDefault注解被应用在所标注元素的作用域内,那么该元素定义默认使用可空性注解,该注解类型自身包含的标注有@Nonnull注解(或其别称)与@TypeQualifierDefault (...)注解,后者带有一到多个ElementType值。该ElementType常见的情况有以下几种。

  • ElementType.METHOD:用于方法的返回值。
  • ElementType.PARAMETER:用于值参数。
  • ElementType.FIELD:用于字段。
  • ElementType.TYPE_USE:该属性自Kotlin 1.1.60版本起,适用于任何类型,包括类型参数、类型参数的上界与通配符类型。

当类型未标注可空性注解时,默认使用可空性注解,而且该默认值是由与最内层标注所用类型相匹配的ElementType类型限定符的默认注解元素确定的。

@Nonnull
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
public @interface NonNullApi {
}
@Nonnull(when = When.MAYBE)
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER, ElementType. TYPE_USE})
public @interface NullableApi {
}
@NullableApi
interface A {
    String foo(String x);   // fun foo(x: String?): String?
    @NotNullApi    //覆盖来自接口的默认值
    String bar(String x, @Nullable String y); // fun bar(x: String, y: String?): String 
    // @NullableApi具有TYPE_USE元素类型
    //因此认为List<String> 类型参数是可空的
    String baz(List<String> x); // fun baz(List<String?>?): String?
    //x参数仍然是平台类型,因为有显式UNKNOWN 标记的可空性注解
    String qux(@Nonnull(when = When.UNKNOWN) String x); // fun baz(x: String!): String?
}

除此之外,类型限定符还支持包级的默认可空性。代码如下。

//文件完整路径test/package-info.java
@NonNullApi   //默认将test包中的所有类型声明为不可空
package test;

3.@UnderMigration注解

自Kotlin 1.1.60版本开始,可以使用@UnderMigration注解来定义可空性类型限定符的迁移状态,使用该功能时需要引入kotlin-annotations-jvm库。

@UnderMigration(status = ...)注解的状态值指定了编译器处理Kotlin中注解类型的用法。该注解状态主要有以下几种情况。

  • MigrationStatus.STRICT:该注解与可空性注解一样,即对不当用法报错并影响注解声明内的类型在Kotlin中的呈现。
  • MigrationStatus.WARN:该注解会对不当的用法报警而不是报错,声明内的类型为平台类型。
  • MigrationStatus.IGNORE:该注解会使编译器完全忽略可空性注解。

在使用@UnderMigration注解时,还可以配合类型限定符别称与类型限定符默认值一起使用。代码如下。

@Nonnull(when = When.ALWAYS)
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
@UnderMigration(status = MigrationStatus.WARN)
public @interface NonNullApi {
   //其他内容
}
//类中的类型是非空的,但是只报警告
//因为@NonNullApi标注了@UnderMigration(status = MigrationStatus.WARN)注解
@NonNullApi 
public class Test {}

可空性注解的迁移状态并不会从其类型限定符别称中继承,而是仅仅遵循默认类型限定符的用法。如果默认类型限定符使用了类型限定符别称,而且它们都使用了@UnderMigration注解,那么该注解将使用默认类型限定符的状态。

4.编译器配置

Kotlin使用-Xjsr305编译器标志来检测JSR-305配置,在Kotlin 1.1.50+/1.2 版本中,默认使用-Xjsr305=warn。

  • -Xjsr305={strict|warn|ignore}:设置非@UnderMigration注解行为,自定义的可空性限定符,其中@TypeQualifierDefault注解已经被用在很多知名开发库中。当用户更新到JSR-305支持的最新Kotlin版本时,可能需要对旧代码进行手动迁移。自Kotlin 1.1.60版本起,该配置只影响非@UnderMigration注解。
  • -Xjsr305=under-migration:{strict|warn|ignore}:自Kotlin 1.1.60版本开始,该配置覆盖@UnderMigration注解相关的行为,使用它可以对库的迁移状态给出不同的错误信息。
  • -Xjsr305=@<fq.name>:{strict|warn|ignore}:自Kotlin 1.1.60版本起,使用该配置将覆盖单个注解的行为,其中<fq.name>是该注解的完整限定类名。不同的注解可以多次出现,这对于管理特定库的状态迁移是非常有用的。

在上述配置中,strict、warn与ignore值的含义与MigrationStatus注解中的值含义是相同的,而且只有strict模式会影响注解声明类型在Kotlin中的呈现。但是,系统内置的JSR-305注解@Nonnull、@Nullable与@CheckForNull总是启用的并影响所注解的声明在Kotlin中的呈现,即使修改-Xjsr305编译器配置标志也是不会有影响的。例如,将-Xjsr305=ignore -Xjsr305= under-migration:ignore [email protected]:warn添加到编译器参数中时,编译器会对由@org.library.MyNullable标注的不当用法生成警告,而忽略其他所有JSR-305注解。