2 回答

TA貢獻(xiàn)1815條經(jīng)驗(yàn) 獲得超6個(gè)贊
Nullness Checker 警告您規(guī)范(類型注釋)與代碼本身不一致。
無效性問題
您的代碼的關(guān)鍵問題在這里:
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
以下是兩個(gè)不兼容的規(guī)范:
put
需要一個(gè)非空的第二個(gè)參數(shù)(回想一下這@NonNull
是默認(rèn)值):
???public?IMap?put(Object?key,?Object?value)?{?...?}
get
可能隨時(shí)返回 null,而客戶端無法知道返回值何時(shí)可能為非 null:
???@Nullable?Object?get(Object?o);
如果你想聲明一個(gè)方法的返回值在一般情況下是可以為空的,但在某些情況下是非空的,那么你需要使用條件后置條件,比如@EnsuresNonNullIf
.
也就是說,Nullness Checker對Map.get
.?您的代碼不使用它,因?yàn)槟鷽]有覆蓋的方法java.util.Map.get
(盡管它確實(shí)有一個(gè)名為的類Map
,與 無關(guān)java.util.Map
)。
如果您想要對 進(jìn)行特殊情況處理IMap.get
,則可以:
你的課程應(yīng)該擴(kuò)展
java.util.Map
,或者您應(yīng)該擴(kuò)展 Nullness Checker 以識別您的班級。
地圖關(guān)鍵問題
你能提供從哪里開始的指導(dǎo)或可以學(xué)習(xí)的例子嗎?
我嘗試隨意地在這里和那里灑一些@KeyFors,但由于缺乏完全理解我在做什么,我可能需要一段時(shí)間才能找到正確的位置;-)
請不要那樣做!那就是痛苦。手冊告訴你不要那樣做;相反,首先思考并編寫描述代碼的規(guī)范。
@KeyFor
以下是您可以編寫的三個(gè)注釋:
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() {
這些注釋分別說明:
迭代器返回此對象的鍵。
客戶端必須為此對象傳遞一個(gè)密鑰。
迭代器返回此對象的鍵。我抑制了警告,因?yàn)榇藢ο蟪洚?dāng)包含對象的包裝器,而且我不記得 Checker Framework 有一種方式說“此對象是一個(gè)字段的包裝器,它的每個(gè)方法都具有相同的屬性作為那個(gè)領(lǐng)域的方法。”
結(jié)果類型檢查沒有問題(此答案第一部分中指出的無效性除外):
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貢獻(xiàn)1841條經(jīng)驗(yàn) 獲得超3個(gè)贊
總結(jié)信息性接受的答案:
無法對給定的代碼示例進(jìn)行注解,使得迭代器和 IMap 的 get 方法之間的語義關(guān)系可以指定給 Checker Framework;
因此,當(dāng)前報(bào)告的錯誤需要本地非空斷言,重寫代碼以避免鍵迭代器或 SuppressWarning 注釋。
如果我們想避免這些變通方法,那么有必要對檢查器框架進(jìn)行擴(kuò)展,例如它是如何針對 java.util.Map 進(jìn)行特殊處理的。
添加回答
舉報(bào)