我最近开始使用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);
}
}
这三个声明中的每一个都是有效的并且编译得很好,但只有最后一个声明会使警告消息消失。
我原本期望第一个有效,因为它实际上注释了方法本身使用的“类型”,并且完全被第二个例子搞糊涂了。
这三个声明之间的语义差异究竟是什么,为什么三个声明有效,而两个声明不起作用?
答案 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。
免责声明:我无法完全重现您的情况,因为我缺少所示方法的类上下文。 MenuBar
,getKey
和backingMap
来自何处?