I get the error...
The return type is incompatible with 'Set<Map.Entry<K,T>>' returned from Map<K,T>.entrySet() (mismatching null constraints)
...when implementing a Map
and overriding Map.entrySet
like this:
package org.abego.util;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
...
public abstract class MyMap<K, T> implements Map<K, T> {
private Map<K, T> map = new LinkedHashMap<>();
@Override
public Set<java.util.Map.Entry<K, T>> entrySet() {
return map.entrySet();
}
...
}
The package org.abego.util
defines the default nullness to be @NonNull
:
@org.eclipse.jdt.annotation.NonNullByDefault
package org.abego.util;
The only way I found to get rid of the error was to 'remove the default nullness annotation' for entrySet
using an @NonNullByDefault({})
annotation:
package org.abego.util;
...
import org.eclipse.jdt.annotation.NonNullByDefault;
public abstract class MyMap<K, T> implements Map<K, T> {
...
@Override
@NonNullByDefault({})
public Set<java.util.Map.Entry<K, T>> entrySet() {
return map.entrySet();
}
...
}
While this does work I am wondering if this is the correct way to fix the error.
(I am using Eclipse 4.5 (Mars) and jdk1.8.0_60.)
You are trying to override this method:
Set<Map.Entry<K, V>> entrySet();
with a method whose effective signature is
@NonNull Set<@NonNull Map.Entry<K, V>> entrySet();
This is an incompatible override as can be demonstrated via this caller:
Set<Map.Entry<Foo,Bar>> entries = someMap.entrySet();
entries.add(null);
If someMap
has type MyMap
then the element type of entries
is @NonNull
, but the contract of java.util.Map.entrySet()
promises a Set
where null
elements are tolerated (set has not specified nullness of its elements). Mixing both contracts (with @NonNull
and unspecified) on the same object entries
will break clients that assume non-null elements.
Looking at the Javadoc of entrySet()
, it should however be safe to assume that Map.entrySet()
will always return a Set
with non-null elements. Hence, the root of the problem is in the fact, that java.util.Map.entrySet()
has no null annotations.
Since Eclipse Mars, this can be overcome by way of external null annotations. You can tell the compiler that the return type of Map.entrySet()
is in fact @NonNull Set<@NonNull Map.Entry<K,V>>
, either by applying the Annotate command in the IDE, or by putting the following snippet into a file java/util/Map.eea
inside a directory that has been configured as the location for external annotations for the JRE:
class java/util/Map
entrySet
()Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;
()L1java/util/Set<L1java/util/Map$Entry<TK;TV;>;>;
With such external annotations in place your program will be accepted by JDT's null analysis.