类型注释属于有界通配符的什么位置?

Where does the Type Annotation belong in a bounded wildcard?

我最近开始使用 Eclipse 的 nullability 注释更新我的 Java 项目。我有一个 JavaFX 基础项目,包含一些翻译 类.

现在,在我的 LocalizedList 中,我用文档树中的一个元素对其进行初始化,然后递归地添加其所有子元素。

@NonNullByDefault
private void locChildren(Styleable c) {
    String localizable = getKey(c);
    if(localizable != null) {
        backingMap.put(c, localizable);
        setText(c, localizable);
    }
    if(c instanceof MenuBar) {
        MenuBar mb = (MenuBar)c;
        initLoc(mb.getMenus());
    } // else if ...
}

@NonNullByDefault
public void initLoc(List<? extends Styleable> s) {
    for(Styleable c : s) {
        locChildren(c);
    }
}

现在,如果我只保留它,我会收到非常长的警告消息

Null type safety (type annotations): The expression of type 'ObservableList<Menu>' needs unchecked conversion to conform to '@NonNull List<? extends @NonNull Styleable>', corresponding supertype is 'List<Menu>'

这是因为 MenuBar#getMenus() 没有使用任何 可空性 注释进行注释,这是意料之中的。


将@Nullable 注解应用到列表本身后,问题并没有解决。因此,我将 @Nullable 添加到通配符中。这是我偶然发现一些令人困惑的地方。

@NonNullByDefault
public void initLoc1(@Nullable List<@Nullable ? extends Styleable> s) {
    for(Styleable c : s) {
        locChildren(c);
    }
}

@NonNullByDefault
public void initLoc2(@Nullable List<@Nullable ? extends @Nullable Styleable> s) {
    for(Styleable c : s) {
        locChildren(c);
    }
}

@NonNullByDefault
public void initLoc3(@Nullable List<? extends @Nullable Styleable> s) {
    for(Styleable c : s) {
        locChildren(c);
    }
}

这三个声明中的每一个都是有效的并且可以正常编译,但是只有最后一个声明使警告消息消失。


我原以为第一个是有效的,因为它实际上注释了方法本身中使用的 "type",并且完全被第二个示例搞糊涂了。

这三个声明之间的语义区别究竟是什么,为什么三个有效,而两个和一个无效?

要理解原始代码示例,有必要了解 @NonNullByDefault, which can be fine tuned using enum DefaultLocation 的确切效果。后者提到

Wildcards and the use of type variables are always excluded from NonNullByDefault.

另一方面,通配符绑定 extends Styleable @NonNullByDefault.

影响

这解释了为什么 initLoc 的预期参数类型是 @NonNull List<? extends @NonNull Styleable>。方法 initLoc3 解决了编译错误,因为它完全覆盖了这两个 @NonNull 注释,如此处所示 - 通过在这些位置显式声明 @Nullable

至于将显式 @Nullable 应用于通配符本身,Eclipse 遵循为 Checkers Framework 提出的 concepts。特别是 @Nullable ? 被解释为具有可空 属性.

的下限和上限

这解释了为什么 initLoc2initLoc3 不同:它定义了一个额外的 lower 边界,然后与实际参数不匹配。

为了解决关于 MenuBar 的问题,您可能需要考虑使用 external annotations.

免责声明:我无法准确重现您的情况,因为我缺少所示方法的 class 上下文。 getKeybackingMapsetText 是从哪里来的?