Cody Casterline: Post

Cody Casterline

I've been writing Java since before Generics and still ran into this landmine:

Coworker (reviewing my code): container.contains(null) can throw a NullPointerException.

Me: I don't think so, the docs say:

Returns true if this collection contains the specified element. More formally, returns true if and only if this collection contains at least one element e such that (o==null ? e==null : o.equals(e)).

And this code works as I expect:

import java.util.*;

public class Contains {
    public static void main(String[] args) {

        // Interestingly, List.of requires all its elements be non-null. Weird.
        // var list1 = List.of("foo", "bar", "baz");
        // var list2 = List.of("foo", "bar", null);

        var list1 = makeCollection("foo", "bar", "baz");
        var list2 = makeCollection("foo", "bar", null);
        
        check(list1);
        check(list2);
    }

    private static Collection<String> makeCollection(String... args) {
        // return Arrays.asList(args);
        return new HashSet<String>(Arrays.asList(args));
    }

    private static void check (Collection<String> list) {
        System.out.println(list.contains(null));
    }
}

Coworker: read a bit further. Docs also say:

Throws […] NullPointerException - if the specified element is null and this collection does not permit null elements (optional)

… sure enough. In my case I'm actually using a Set.of(predefined, elements), and that particular implementation will throw if passed a null.

UGHHh. NULLS.

FWIW, Kotlin handles this much more nicely:

fun main() {
    val c1 = setOf("foo", "bar")
    val c2 = setOf("foo", null)

    
    val value: String? = null
    println(c1.contains(value))
    println(c2.contains(value))
}

… though you can only depend on that sane behavior when using its setOf() constructor. If you might ever be passed a non-null-safe Java Collection you're back to needing to protect yourself against NPEs.