Type Annotation属于有界通配符在哪里?

时间:2016-07-12 21:42:02

标签: java generics annotations nullable 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注释应用于List本身后,问题仍未解决。所以,我将@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);
    }
}

这三个声明中的每一个都是有效的并且编译得很好,但只有最后一个声明会使警告消息消失。

我原本期望第一个有效,因为它实际上注释了方法本身使用的“类型”,并且完全被第二个例子搞糊涂了。

这三个声明之间的语义差异究竟是什么,为什么三个声明有效,而两个声明不起作用?

1 个答案:

答案 0 :(得分:1)

要理解原始代码示例,有必要了解@NonNullByDefault的确切效果,可以使用枚举DefaultLocation对其进行微调。后者提到

  

通常从NonNullByDefault中排除通配符和类型变量的使用。

另一方面,通配符extends Styleable 受<{1}}影响

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

至于将明确的@Nullable应用于通配符本身,Eclipse遵循为{Checkers Framework>提出的concepts。特别是@Nullable被解释为具有可为空的属性的下限和上限。

这解释了为什么@Nullable ?initLoc2不同:它定义了一个额外的 lower 边界,然后由实际参数无法匹配。

为了解决有关initLoc3的问题,您可以考虑使用external annotations

免责声明:我无法完全重现您的情况,因为我缺少所示方法的类上下文。 MenuBargetKeybackingMap来自何处?