Java Map w @NonNull值类型和@Nullable get()方法结果?

时间:2014-08-18 20:25:11

标签: java null annotations nullable

我有一个方法返回一个自定义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值?

非常感谢。

4 个答案:

答案 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/