jan
jan

Reputation: 2830

How shall one implement the null object pattern in generics?

I like the idea of the null object pattern and force me to use it until it really feels normal and good. At the moment I don't see how to use it in generic types. I know the possibility to define a second generic type and to pass in the class to construct a default object, but this feels really too much overhead for this pattern. Is there a good way?

public class GenericExample<T> {
    public static final GenericExample NULL = ???;

    private T attribute;

    public GenericExample(T attribute) {
        this.attribute = attribute;
    }
}

public class HoldGeneric {
    private GenericExample<String> name = GenericExample.NULL;

    public initLater(String name) {
        this.name = new GenericExample<String>(name);
    }
}

Upvotes: 5

Views: 2908

Answers (3)

SimonC
SimonC

Reputation: 6718

Without passing in the class of T there's no way to properly implement it. The closest you could do, abusing type erasure, would be:

class GenericExample<T>
{
  private static final GenericExample<Object> NULL = new GenericExample<Object>(); 

  public static <T> GenericExample<T> nil()
  {
    @SuppressWarnings("unchecked")
    final GenericExample<T> withNarrowedType = (GenericExample<T>)NULL;
    return withNarrowedType;
  }
}

However, you have to accept that GenericExample.<Apple>nil() would be the same as GenericExample.<Orange>nil().

Upvotes: 1

coffee_machine
coffee_machine

Reputation: 1223

Based on my understanding of your question, I would go with the following.

public class GenericExample<T> {

    public static final GenericExample<?> NULL = new GenericExample<Object>(new Object()) {
        public void print() {
        }
    };

    private T attribute;

    public GenericExample(T attribute) {
        this.attribute = attribute;
    }

    public void print() {
        System.out.print(attribute.toString());
    }

    @SuppressWarnings("unchecked")
    public static <X> GenericExample<X> nil() {
        return (GenericExample<X>) NULL;
    }
}

public class Test {

    private static GenericExample<String> name = GenericExample.nil();

    public static void main(String[] args) {
        String x = "blah";
        name.print();
        if (name == GenericExample.NULL) {
            name = new GenericExample<String>(x);
        }
        name.print();
    }
}

Upvotes: 2

dkatzel
dkatzel

Reputation: 31648

You can follow what the JDK does and use a static method to infer the generic type and do an unchecked cast.

java.util.Collections implements Null objects for empty lists and sets. In Java Pre-generics, there is a public static field.

public static final List EMPTY_LIST = new EmptyList();

Post generics: There are now static methods that infer the generic type and perform the unchecked casting for you to cast it to the correct type. The type doesn't really matter since the collections are empty but it makes the compiler happy.

@SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}

Upvotes: 4

Related Questions