Write a novel about the nullness of the `V` types in `getOrDefault`.
RELNOTES=n/a
PiperOrigin-RevId: 423822183
diff --git a/android/guava/src/com/google/common/collect/ImmutableMap.java b/android/guava/src/com/google/common/collect/ImmutableMap.java
index 648b3fc..671c301 100644
--- a/android/guava/src/com/google/common/collect/ImmutableMap.java
+++ b/android/guava/src/com/google/common/collect/ImmutableMap.java
@@ -828,6 +828,31 @@
// @Override under Java 8 / API Level 24
@CheckForNull
public final V getOrDefault(@CheckForNull Object key, @CheckForNull V defaultValue) {
+ /*
+ * Even though it's weird to pass a defaultValue that is null, some callers do so. Those who
+ * pass a literal "null" should probably just use `get`, but I would expect other callers to
+ * pass an expression that *might* be null. This could happen with:
+ *
+ * - a `getFooOrDefault(@Nullable Foo defaultValue)` method that returns
+ * `map.getOrDefault(FOO_KEY, defaultValue)`
+ *
+ * - a call that consults a chain of maps, as in `mapA.getOrDefault(key, mapB.getOrDefault(key,
+ * ...))`
+ *
+ * So it make sense for the parameter (and thus the return type) to be @Nullable.
+ *
+ * Two other points:
+ *
+ * 1. We'll want to use something like @PolyNull once we can make that work for the various
+ * platforms we target.
+ *
+ * 2. Kotlin's Map type has a getOrDefault method that accepts and returns a "plain V," in
+ * contrast to the "V?" type that we're using. As a result, Kotlin sees a conflict between the
+ * nullness annotations in ImmutableMap and those in its own Map type. In response, it considers
+ * the parameter and return type both to be platform types. As a result, Kotlin permits calls
+ * that can lead to NullPointerException. That's unfortunate. But hopefully most Kotlin callers
+ * use `get(key) ?: defaultValue` instead of this method, anyway.
+ */
V result = get(key);
// TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker.
if (result != null) {
diff --git a/guava/src/com/google/common/collect/ImmutableMap.java b/guava/src/com/google/common/collect/ImmutableMap.java
index d4c87c3..2f8852a 100644
--- a/guava/src/com/google/common/collect/ImmutableMap.java
+++ b/guava/src/com/google/common/collect/ImmutableMap.java
@@ -971,6 +971,31 @@
@Override
@CheckForNull
public final V getOrDefault(@CheckForNull Object key, @CheckForNull V defaultValue) {
+ /*
+ * Even though it's weird to pass a defaultValue that is null, some callers do so. Those who
+ * pass a literal "null" should probably just use `get`, but I would expect other callers to
+ * pass an expression that *might* be null. This could happen with:
+ *
+ * - a `getFooOrDefault(@Nullable Foo defaultValue)` method that returns
+ * `map.getOrDefault(FOO_KEY, defaultValue)`
+ *
+ * - a call that consults a chain of maps, as in `mapA.getOrDefault(key, mapB.getOrDefault(key,
+ * ...))`
+ *
+ * So it make sense for the parameter (and thus the return type) to be @Nullable.
+ *
+ * Two other points:
+ *
+ * 1. We'll want to use something like @PolyNull once we can make that work for the various
+ * platforms we target.
+ *
+ * 2. Kotlin's Map type has a getOrDefault method that accepts and returns a "plain V," in
+ * contrast to the "V?" type that we're using. As a result, Kotlin sees a conflict between the
+ * nullness annotations in ImmutableMap and those in its own Map type. In response, it considers
+ * the parameter and return type both to be platform types. As a result, Kotlin permits calls
+ * that can lead to NullPointerException. That's unfortunate. But hopefully most Kotlin callers
+ * use `get(key) ?: defaultValue` instead of this method, anyway.
+ */
V result = get(key);
// TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker.
if (result != null) {