我有一个方法返回一个自定义Map实现,其条目完全由非空键和值组成,所以我想添加类型注释,以指示客户端可以迭代Map.Entry' s无需检查它们是否为空值:Map< @NonNull String,@ NonNull String>
问题是,Map.get方法API指定为任何尝试检索Map中不存在的键的值而返回null,并注释我的get方法实现以返回@Nullable String生成一个编译器警告,表明返回类型与Map指定的@NonNull返回不兼容。
据我所知,今天创建的Map.get API可能会返回java.util.Optional结果或抛出NoSuchElementException,但是,对于现有的Collections API,是否可以保持与get方法规范的兼容性还指定我的Map只包含@NonNull值?
非常感谢。
答案 0 :(得分:1)
不幸的是,Map API与null注释并不真正兼容。 Map.get返回泛型类型V,即使将V定义为@NonNull,也会违反API,因为null必须是允许的返回值。
这是一个known limitation的空注释,可能只有在实现nullity profiles for libraries时才能解决。在此之前,唯一的解决方法是在获取值之前检查Map.containsKey,而不是之后检查值为null,或者只是避免在地图值类型上使用@NonNull。
答案 1 :(得分:0)
您似乎指的是Eclipse的无效性检查实现。你是对的,它没有准确地说明对Map.get
的调用。
如果有关Map.get
来电的更准确推理对您很重要,您可能需要考虑使用其他无效检查程序。例如,构建在Checker Framework上的Nullness Checker处理您的案例。如果the key is in the map并且地图的所有元素都为非空,它会将Map.get
的返回值视为非空。
答案 2 :(得分:0)
这是一个简单的库,可以用作解决方法:
package xxxx;
import java.util.*;
import org.eclipse.jdt.annotation.*;
public class NullUtil
{
@SuppressWarnings ("null")
public static <@NonNull K, @NonNull T> @NonNull Optional<@Nullable T> get (@NonNull Map<@NonNull K, @NonNull T> m, @NonNull K key)
{
return Optional.ofNullable(m.get(key));
}
@SuppressWarnings ("null")
public static <@NonNull K, @NonNull T> @NonNull T getOrThrow (@NonNull Map<@NonNull K, @NonNull T> m, @NonNull K key)
{
return Optional.ofNullable(m.get(key)).orElseThrow(
() -> new RuntimeException("Map does not contain element '" + key + "'"));
}
@SuppressWarnings ("null")
public static <@NonNull K, @NonNull T> @Nullable T getOrNull (@NonNull Map<@NonNull K, @NonNull T> m, @NonNull K key)
{
return Optional.ofNullable(m.get(key)).orElse(null);
}
}
以下是一些示例用法:
package xxxx;
import static org.junit.Assert.*;
import java.util.*;
import org.eclipse.jdt.annotation.NonNull;
import org.junit.*;
public class NullUtilTest
{
@Before
public void setup ()
{
_m.put("a", "va");
_m.put("b", "vb");
_m.put("c", "vc");
}
@Test
@SuppressWarnings ("null")
public void testOptional ()
{
assertTrue(NullUtil.get(_m, "a").isPresent());
assertFalse(NullUtil.get(_m, "x").isPresent());
assertEquals("va", NullUtil.get(_m, "a").orElse(null));
assertEquals(null, NullUtil.get(_m, "x").orElse(null));
}
@Test
public void testOrNull ()
{
assertEquals("va", NullUtil.getOrNull(_m, "a"));
assertEquals(null, NullUtil.getOrNull(_m, "x"));
}
@Test
public void testOrThrow ()
{
assertEquals("va", NullUtil.getOrThrow(_m, "a"));
try
{
assertEquals("vx", NullUtil.getOrThrow(_m, "x"));
fail();
}
catch (Exception e)
{
assertEquals("Map does not contain element 'x'", e.getMessage());
}
}
private final @NonNull Map<@NonNull String, @NonNull String> _m = new HashMap<>();
}
答案 3 :(得分:0)
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
当您使用空值或键出现此错误时:
c.f.j.c.JsonGenerationException:
Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)
at c.f.j.d.s.i.FailingSerializer.serialize(FailingSerializer.java:36)
您可以解决覆盖Serialize方法,例如:
class MySerializer extends StdSerializer<Object> {
public MyDtoNullKeySerializer() {
this(null);}
public MySerializer(Class<Object> t) {
super(t);
}
@Override
public void serialize(Object nullKey, JsonGenerator jsonGenerator,SerializerProvider unused)
throws IOException, JsonProcessingException {
jsonGenerator.writeFieldName("");
}
}
有关完整示例,请参见此处: https://lentux-informatica.com/jackson-problema-con-maps-e-valori-null/