ECLIPSE将其显示为Java问题:

此处不能应用未指定显式目标元素类型的批注类型.

这只发生在我从Eclipse 2022-09升级到2023-09的时候.

这是发生这种情况的一个示例:

public <T> @Nullable Set<T> getSomeSet(T something) {
  // ...
}

注释是jsr305库中的javax.annotation.Nullable.

我使用的是Adoptium OpenJDK 17.0.8.101-HotSpot.

  • 但在《2022-09年的日食》中没有出现这种情况.
  • 这不会发生在IntelliJ身上.
  • 当我通过gradlew compileJava编译时,不会发生这种情况.
  • 这不会发生在public @Nullable Set<?> getSomeSet() { ... }
  • 这不会发生在public @Nullable <T> Set<T> getSomeSet(T something)

我不明白为什么现在在这种特定情况下会发生这种情况(而不是在所有其他情况下),以及我可以做些什么才能使该错误从我的Eclipse IDE中消失.我在首选项中找不到任何选项来禁用这种错误.

EDIT:简化了最小复制情况,并增加了"不会发生"情况#4.

EDIT2:增加了"没有发生"的情况#5.

推荐答案

可能看起来像是这些声明

public <T> @Nullable Set<T> getSomeSet(T something)
public @Nullable <T> Set<T> getSomeSet(T something)

在语法上是不同的,即在第一个变体中,注释绑定到返回类型.显然,《月食》遵循了这个 idea .

然而,grammar of the specification将两者都显示为方法声明的一部分:

MethodDeclaration:
    {MethodModifier} MethodHeader MethodBody
MethodHeader:
    Result MethodDeclarator [Throws]
    TypeParameters {Annotation} Result MethodDeclarator [Throws]
MethodDeclarator:
    Identifier ( [ReceiverParameter ,] [FormalParameterList] ) [Dims]

MethodModifier

MethodModifier:
    (one of)
    Annotation public protected private
    abstract static final synchronized native strictfp

请注意,TypeParametersResult之间的{Annotation}MethodHeader的一部分.

然后这Java Language Specification, §9.7.4个人说:

批注可能出现在程序中的语法位置,在那里它可能合理地应用于声明、类型或两者.

[.]

Java编程语言的语法明确地将这些位置的注释视为声明的修饰符(§8.3),但这纯粹是一个语法问题.批注是应用于声明还是应用于已声明实体的类型--因此,批注是声明批注还是类型批注--取决于批注接口[…]的适用性

我理解这一点的方式是,语法位置不应该重要,重要的是批注的适用性,由@Target批注或其缺失给出.这也是javac处理这件事的方式,不同于Eclipse:

public class AnnotationExample {
    public static void main(String[] args) throws NoSuchMethodException {
        Method m = AnnotationExample.class.getMethod("methodName");
        System.out.println("method:      " + Arrays.toString(m.getAnnotations()));
        System.out.println("return type: "
            + Arrays.toString(m.getAnnotatedReturnType().getAnnotations()));
    }

    @Retention(RetentionPolicy.RUNTIME) @Target({TYPE_USE, METHOD}) @interface A {}
    @Retention(RetentionPolicy.RUNTIME) @Target({TYPE_USE, METHOD}) @interface B {}

    public static @A <T extends Enum<T>> @B Set<T> methodName() {
        return null;
    }
}

当编译为javac时,它将打印

method:      [@AnnotationExample$A(), @AnnotationExample$B()]
return type: [@AnnotationExample$A(), @AnnotationExample$B()]

但在使用Eclipse编译时,它会打印

method:      [@AnnotationExample.A()]
return type: [@AnnotationExample.A(), @AnnotationExample.B()]

表示Eclipse仅将类型参数声明后面的批注视为返回类型的批注.因此,将B‘S声明更改为@Target(METHOD)会在Eclipse中产生编译错误.


此行为与未给出@Target注释时假定有效的目标交互:

从Java 8到Java 13,the specification looked like

如果在批注类型T的声明中不存在类型为java.lang.annotation.Target的批注,则T适用于除类型参数声明之外的所有声明上下文,并且不适用于任何类型上下文.

从Java开始,17,the specification looks like

如果在注释接口A的声明中不存在类型为java.lang.annotation.Target的注释,则A适用于所有声明上下文,而不适用于任何类型上下文.

因此,这看起来像是一个直接的更改,现在还允许在没有给出@Target的情况下将类型参数声明作为目标,这不会影响我们的场景.

如果没有中间版本,14 to 16:

如果在注释接口A的声明中没有出现类型为java.lang.Annotation.Target的注释,则A适用于所有9个声明上下文和所有16个类型上下文.

(在Java 16中,有10个声明上下文和17个类型上下文)

假设类型参数声明后面的批注仅是类型上下文,这将直接影响编译器.由于这比后续版本更宽松,编译器供应商对此感到惊讶并不得不在更新中提供更多限制行为是可以理解的.

(请注意,javac通过从一开始就没有实现这些更改来躲避这一点,JDK-21是第一个实现自Java 17年以来指定的行为的,见JDK-8309743)


重要的是要记住,虽然不应该发生编译器错误,但规范更改仍然会影响判断代码时报告的注释内容.当您想要能够注释类型而不是方法时,您需要显式的@Target,包括TYPE_USE.

Java相关问答推荐

无法运行Java(已解决)

那么比较似乎不是词典学的,尽管doctor 这么说

使用Apache Poi MQLSlideShow,在XSLFTable表中,我们可以在文本段落后面的每个单元格中包含圆角矩形吗?

在spring—data中自动发现native—sql查询期间遇到重复的SQL别名[id]

在bash中将数组作为Java程序的参数传递

只需最少的代码更改即可将版本号标记添加到日志(log)

JPanel透支重叠的JComcoBox

如何在Spring Java中从数据库列中获取JSON中的具体数据

如何将其他属性引用到log4j2 yaml配置中?

匹配一组字符或另一组字符

我怎样才能让IntelliJ标记toString()的任何实现?

Domino中不同的java.Protocol.handler.pkgs设置在XPages Java中导致错误

如何在字节数组中反转UTF-8编码?

如何在Spring Security中设置一个任何人都可以打开的主页?

将基于实例编号的对象列表拆分为新的对象列表

如何以事务方式向ibmmq发送消息

ResponseEntity.控制器截断响应的JSON部分

为什么我得到默认方法的值而不是被覆盖的方法的值?

ANTLR 接受特殊字符,例如 .标识符或表达式中的(点)和 ,(逗号)

Java ModbusRTU写寄存器