2 回答

TA貢獻1815條經驗 獲得超6個贊
Nullness Checker 警告您規范(類型注釋)與代碼本身不一致。
無效性問題
您的代碼的關鍵問題在這里:
tmp.put(k, get(k))
錯誤信息是:
error: [argument.type.incompatible] incompatible types in argument.
? ? ? ? ? ? ? ?tmp.put(k, get(k)); // get(k) is always non-null because of the key iterator
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?^
? found? ?: @Initialized @Nullable Object
? required: @Initialized @NonNull Object
以下是兩個不兼容的規范:
put
需要一個非空的第二個參數(回想一下這@NonNull
是默認值):
???public?IMap?put(Object?key,?Object?value)?{?...?}
get
可能隨時返回 null,而客戶端無法知道返回值何時可能為非 null:
???@Nullable?Object?get(Object?o);
如果你想聲明一個方法的返回值在一般情況下是可以為空的,但在某些情況下是非空的,那么你需要使用條件后置條件,比如@EnsuresNonNullIf
.
也就是說,Nullness Checker對Map.get
.?您的代碼不使用它,因為您沒有覆蓋的方法java.util.Map.get
(盡管它確實有一個名為的類Map
,與 無關java.util.Map
)。
如果您想要對 進行特殊情況處理IMap.get
,則可以:
你的課程應該擴展
java.util.Map
,或者您應該擴展 Nullness Checker 以識別您的班級。
地圖關鍵問題
你能提供從哪里開始的指導或可以學習的例子嗎?
我嘗試隨意地在這里和那里灑一些@KeyFors,但由于缺乏完全理解我在做什么,我可能需要一段時間才能找到正確的位置;-)
請不要那樣做!那就是痛苦。手冊告訴你不要那樣做;相反,首先思考并編寫描述代碼的規范。
@KeyFor
以下是您可以編寫的三個注釋:
interface IMap extends Iterable<@KeyFor("this") Object> {
...
? ? default IMap remove(@KeyFor("this") Object key) {
...
? ? @SuppressWarnings("keyfor") // a key for `contents` is a key for this object
? ? public java.util.Iterator<@KeyFor("this") Object> iterator() {
這些注釋分別說明:
迭代器返回此對象的鍵。
客戶端必須為此對象傳遞一個密鑰。
迭代器返回此對象的鍵。我抑制了警告,因為此對象充當包含對象的包裝器,而且我不記得 Checker Framework 有一種方式說“此對象是一個字段的包裝器,它的每個方法都具有相同的屬性作為那個領域的方法。”
結果類型檢查沒有問題(此答案第一部分中指出的無效性除外):
import org.checkerframework.checker.nullness.qual.KeyFor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
interface IMap extends Iterable<@KeyFor("this") Object> {
? ? @Nullable Object get(Object o);
? ? IMap put(Object key, Object value); // immutable put
? ? IMap empty();
? ? default IMap remove(@KeyFor("this") Object key) {
? ? ? ? IMap tmp = empty();
? ? ? ? for (Object k : this) {
? ? ? ? ? ? if (!k.equals(key)) {
? ? ? ? ? ? ? ? tmp.put(k, get(k)); // get(k) is always non-null because of the key iterator
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return tmp;
? ? }
}
class Map implements IMap {
? ? java.util.Map<Object, Object> contents = new java.util.HashMap<>();
? ? public Map() {}
? ? private Map(java.util.Map<Object, Object> contents) {
? ? ? ? this.contents = contents;
? ? }
? ? @Override
? ? public @Nullable Object get(Object key) {
? ? ? ? return contents.get(key);
? ? }
? ? @Override
? ? public IMap empty() {
? ? ? ? return new Map();
? ? }
? ? @Override
? ? public IMap put(Object key, Object value) {
? ? ? ? java.util.Map<Object, Object> newContents = new java.util.HashMap<>();
? ? ? ? newContents.putAll(contents);
? ? ? ? newContents.put(key, value);
? ? ? ? return new Map(newContents);
? ? }
? ? @Override
? ? @SuppressWarnings("keyfor") // a key for `contents` is a key for this object
? ? public java.util.Iterator<@KeyFor("this") Object> iterator() {
? ? ? ? return contents.keySet().iterator();
? ? }
}

TA貢獻1841條經驗 獲得超3個贊
總結信息性接受的答案:
無法對給定的代碼示例進行注解,使得迭代器和 IMap 的 get 方法之間的語義關系可以指定給 Checker Framework;
因此,當前報告的錯誤需要本地非空斷言,重寫代碼以避免鍵迭代器或 SuppressWarning 注釋。
如果我們想避免這些變通方法,那么有必要對檢查器框架進行擴展,例如它是如何針對 java.util.Map 進行特殊處理的。
添加回答
舉報