首页 文章

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

提问于
浏览
1

我有一个方法返回一个自定义Map实现,其条目完全由非空键和值组成,所以我想添加类型注释,以指示客户端可以迭代Map.Entry而不必检查它们是否为null values: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

    不幸的是,Map API与null注释并不真正兼容 . Map.get返回泛型类型V,即使将V定义为@NonNull,也会违反API,因为null必须是允许的返回值 .

    这是一个known limitation的空注释,可能只有在实现nullity profiles for libraries时才能解析 . 在此之前,唯一的解决方法是在获取值之前检查Map.containsKey而不是之后检查值为null,或者只是避免在 Map 值类型上使用@NonNull .

  • 1

    您似乎在指的是Eclipse的无效性检查实现 . 你是对的,它没有准确地说明对 Map.get 的调用 .

    如果对 Map.get 的调用的更精确推理对您很重要,您可能需要考虑使用不同的无效检查器 . 例如,构建在Checker Framework上的Nullness Checker处理您的案例 . 如果the key is in the map并且映射的所有元素都为非null,则将 Map.get 的返回值视为非null .

  • 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<>();
    }
    
  • 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/

相关问题