【问题标题】:What's the meaning of java.util.@Nullable?java.util.@Nullable 是什么意思?
【发布时间】:2019-10-09 03:16:14
【问题描述】:

我正在阅读the code of Guava,我在某些代码中找到了注释java.util.@Nullable。我知道@Nullable的意思,但是这个我不明白。特别是,我在包java.util 中找不到名为Nullable 的类。 请有人告诉我这个java.util.@Nullable是什么意思:

public static <T> java.util.@Nullable Optional<T> toJavaUtil(
    @Nullable Optional<T> googleOptional) {
  return googleOptional == null ? null : googleOptional.toJavaUtil();
}

【问题讨论】:

  • 我想知道这是否只是一种奇怪的 Java 语法主义。特别是,这基本上只是意味着public static &lt;T&gt; @Nullable java.util.Optional&lt;T&gt; toJavaUtil,但Java 就是不允许你这样写吗?
  • @JosephSible public static &lt;T&gt; @Nullable java.util.Optional&lt;T&gt; toJavaUtil 确实是不可能的,但是当导入已经存在时,没有理由使用Optional 的FQN。
  • @Tom 还有另一个Optional 在此范围内。
  • 谢谢大家,我在 Checker Framework 中找到了这个。 @Nullable java.util.List The correct Java syntax to write an annotation on a fully-qualified type name is to put the annotation on the simple name part, as in java.util.@Nullable List. But, it’s usually better to add import java.util.List to your source file, so that you can just write @Nullable List.

标签: java


【解决方案1】:

public static &lt;T&gt; java.util.@Nullable Optional&lt;T&gt; toJavaUtil 行是这样写的,因为通常的样式public static &lt;T&gt; @Nullable java.util.Optional&lt;T&gt; toJavaUtil 是无效的。这是在JLS §9.7.4 中定义的:

如果类型 T 的注解适用于类型上下文中的类型(或类型的任何部分),并且 T 适用于类型上下文,并且该注解是不可接受的,则会出现编译时错误.

例如,假设注释类型 TA 仅使用 @Target(ElementType.TYPE_USE) 进行元注释。术语@TA java.lang.Objectjava.@TA lang.Object 是非法的,因为与@TA 最接近的简单名称被归类为包名称。另一方面,java.lang.@TA Object 是合法的。

org.checkerframework.checker.nullness.qual@Nullable的类型声明为:

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

所以它适用于这条规则。

这个结构不会中断执行,因为包java.util和类名Optional被分割了,当我们使用javap -c [compiled class name]查看编译代码时可以看到:

class just.a.test.Main {
  just.a.test.Main();
    Code:
       0: aload_0
       1: invokespecial #1          // Method java/lang/Object."<init>":()V
       4: return

  public static <T> java.util.Optional<T> toJavaUtil(blub.Optional<T>);
    Code:
       0: aload_0
       1: ifnonnull     8
       4: aconst_null
       5: goto          12
       8: aload_0
       9: invokevirtual #2          // Method blub/Optional.toJavaUtil:()Ljava/util/Optional;
      12: areturn
}

blub.Optional 是一个本地类,我在其中复制了 Guava 代码,以便获得一个最小的示例来反/编译)

如您所见,注释不再存在。它只是一个标记,用于在方法返回 null 时阻止编译器发出警告(以及对源代码阅读器的提示),但它不会包含在编译代码中。


此编译器错误也适用于以下变量:

private @Nullable2 java.util.Optional<?> o;

但是当注解另外获得目标类型ElementType.FIELD时可以变得可以接受,如在同一个JLS子句中所写:

如果 TA 还使用 @Target(ElementType.FIELD) 进行元注释,则术语 @TA java.lang.Object 在声明和类型上下文的位置是合法的,例如字段声明 @TA java.lang.Object f;。这里,@TA 被认为适用于 f 的声明(而不适用于 java.lang.Object 类型),因为 TA 适用于字段声明上下文。

【讨论】:

    【解决方案2】:

    当使用注解时,这是当你想为类型编写完全限定名时使用的语法,而不是添加 import 语句。

    引用checker framework manual:

    在完全限定上编写注解的正确 Java 语法 type name 是将注解放在简单的名称部分,如 java.util.@Nullable 列表。但是,通常最好添加导入 java.util.List 到你的源文件,这样你就可以写 @Nullable 列表。

    JSR308规范的第2页也提到了,可以下载here。它说:

    类型注释出现在类型的简单名称之前,如 @NonNull 字符串或 java.lang.@NonNull 字符串。

    【讨论】:

      【解决方案3】:

      这里的奇怪之处在于应用ElementType.TYPE_USE-targeted 注释的语法不熟悉。 如果您查看Nullable 的文档,您会看到不熟悉的目标:

      ...
      @Target(value={TYPE_USE,TYPE_PARAMETER})
      <public @interface Nullable
      ...
      

      此注解在被注解类型的简单名称之前使用,如下所示:

      public static <T> @Nullable Optional<T> toJavaUtil
      public static <T> java.util.@Nullable Optional<T> toJavaUtil
      

      我不知道这种目标的用途是什么,所以在快速阅读之后,我得到了这个简单的示例,它使用具有该目标的注释来获取返回类型元数据:

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.TYPE_USE)
      @interface InterceptedReturnValue {
          boolean value() default true;
      }
      

      并使用以下方法对其进行处理:

      public java.lang.@InterceptedReturnValue(true) String testMethod(String param) {
          return null;
      }
      
      public @InterceptedReturnValue(false) String testMethod(int param) {
          return null;
      }
      
      public static void main(String[] args) throws Exception {
          Method m = Main.class.getDeclaredMethod("testMethod", String.class);
          if(m.getAnnotatedReturnType().isAnnotationPresent(InterceptedReturnValue.class)) {
              InterceptedReturnValue config = m.getAnnotatedReturnType()
                      .getAnnotation(InterceptedReturnValue.class);
      
              if(config.value()) {
                  //logging return value enabled
              }
          }
      }
      

      我相信很多有用的框架,比如checkerframework,都最适合使用ElementType.TYPE_USE

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-06-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-12
        • 2017-06-11
        • 2018-03-05
        • 2023-03-27
        相关资源
        最近更新 更多