[JAVA] Various confusing stories with Map # computeIfAbsent

Map # computeIfAbsent is a method added in Java8, and it's been 5 years, but I knew it about a year ago.

V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)

If the value corresponding to key does not exist, mappingFunction is called and the return value is stored in Map and returned.

ConcurrentHashMap Questions arose when implementing the process of caching data on a Web system.

private Map<String, String> cache = new ConcurrentHashMap<>()

public String get(String id) {
  return cache.computeIfAbsent(key, key -> {
    if (/*External file corresponding to the key exists*/) {
      //Read the file and return it
      return readData;
    }
    //There is no corresponding file
    return null;
  });
}

Is it okay to return null when ConcurrentHashMap can't have null in the value? So I experimented with the test code.

Map<String, String> map = new ConcurrentHashMap<>();
String ret = map.computeIfAbsent("test", key -> null);
System.out.println(ret);

The execution result was no exception and the return value was null. This is in Javadoc It was written properly. .. ..

If the specified key is not yet associated with a value, try to calculate that value using the specified mapping function and
Enter it in this map if it is not null.

Kotlin As you can see, the above is written in Java, but since system development is also in the early stages, when I rewrote the code to Kotlin, a compile error occurred. The version of Kotlin is 1.3.31.

val cache = ConcurrentHashMap<String, String>()

fun get(id: String): String? {
  return cache.computeIfAbsent(id) {
    if (/*External file corresponding to the key exists*/) {
      //Read the file and return it
      return readData;
    }
    return null; //error! Kotlin: Null can not be a value of a non-null type String
  }
}

According to Kotlin's API reference, Map.get

operator fun get(key: K): V?

And V allow nulls, but the return value of the mappingFunction seems to remain V. As a workaround

val ret: String? = map.computeIfAbsent("test") { null!! }

If you add !! like, the compile error will be solved, but kotlin.KotlinNullPointerException will occur at runtime. .. .. orz

As a solution

val map = ConcurrentHashMap<String, String?>()
val ret: String? = map.computeIfAbsent("test") { null }

I only knew about ** declaring V as nullable from the beginning **. However, if I was worried that the return value of get would be a strange type with this, there was no such thing as a matter of course, and I have not found any inconvenience by using String? I see.

Recommended Posts

Various confusing stories with Map # computeIfAbsent