javagenericsadts

Hiding a "local" type parameter in Java


Suppose I'm using an interface with a generic type parameter

interface Foo<T> {
  T getOne();
  void useOne(T t);
}

The intention is that the type T is abstract: it enforces a type constraint on implementations of Foo, but the client code doesn't care exactly what T is.

This is no problem in the context of a generic method:

public <T> void doStuff(Foo<T> foo) {
  T t = foo.getOne();
  /* do stuff */
  foo.useOne(t);
}

But suppose I want to break up the work of doStuff, saving some state in a class Bar. In this case, I seem to need to add the type parameter of Foo to Bar.

public class Bar<T> {
  private Foo<T> foo;
  private T t;

  /* ... */

  public void startStuff() {
    t = foo.getOne();
  }

  public void finishStuff() {
    foo.useOne(t);
  }
}

This is kind of weird, since the type parameter T does not appear in the public interface of Bar (i.e., it is not included in any method parameter or return type). Is there a way to "quantify T away"? I.e., can I arrange for the parameter T to be hidden in the interface of Bar, as in the following?

public class Bar {
  <T> { // foo and t have to use the same T
    private Foo<T> foo;
    private T t;
  } // T is out of scope
  ... 
}

Solution

  • Your problem is similar to that solved by a "capture helper", but I'm not sure it can be applied to your second example where two separate methods are used. Your first doStuff method could definitely be better written as public void doStuff(Foo<?> foo), since it works regardless of Foo type parameter. Then, the "capture helper" pattern would be useful.


    Update: after tinkering a bit, extending the idea of Goetz's capture helper, I came up with this. Inside, it looks a little messy; from the outside, you wouldn't suspect a thing.

    public class Bar {
      private final Helper<?> helper;
      public Bar(Foo<?> foo) {
        this.helper = Helper.create(foo);
      }
      public void startStuff() {
        helper.startStuff();
      }
      public void finishStuff() {
        helper.finishStuff();
      }
      private static class Helper<T> {
        private final Foo<T> foo;
        private T t;
        private Helper(Foo<T> foo) {
          this.foo = foo;
        }
        static <T> Helper<T> create(Foo<T> foo) {
          return new Helper<T>(foo);
        }
        void startStuff() {
          t = foo.getOne();
        }
        void finishStuff() {
          foo.useOne(t);
        }
      }
    }