在Java中隐藏"本地"类型参数

Chr*_*way 9 java generics adts

假设我使用的是具有泛型类型参数的接口

interface Foo<T> {
  T getOne();
  void useOne(T t);
}
Run Code Online (Sandbox Code Playgroud)

意图是类型T是抽象的:它对实现强制执行类型约束Foo,但客户端代码并不关心究竟是什么T.

这在通用方法的上下文中没有问题:

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

但是假设我想要分解工作doStuff,在课堂上保存一些状态Bar.在这种情况下,我似乎需要添加的类型参数FooBar.

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

  /* ... */

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

  public void finishStuff() {
    foo.useOne(t);
  }
}
Run Code Online (Sandbox Code Playgroud)

这有点奇怪,因为类型参数T没有出现在公共接口中Bar(即,它不包含在任何方法参数或返回类型中).有没有办法"量化T"?即,我可以安排将参数T隐藏在界面中Bar,如下所示吗?

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
  ... 
}
Run Code Online (Sandbox Code Playgroud)

Tom*_*ine 6

为了有用,在某些时候你将设置该foo字段.此时你应该知道(或能够捕捉)T.我建议在构造函数中执行此操作,然后Bar有一个泛型参数是有意义的.您甚至可以使用接口,因此客户端代码不必查看类型.但是,我认为你不会接受我的建议,真的想要一个setFoo.所以只需为可切换的实现添加一点:

/* pp */ class final BarImpl<T> {
    private final Foo<T> foo;
    private T t;

    BarImpl(Foo<T> foo) {
        this.foo = foo;
    }

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

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

public final class Bar {
    private BarImpl<?> impl;

    /* ... */

    // Need to capture this wildcard, because constructors suck (pre-JDK7?).
    public void setFoo(Foo<?> foo) {
        setFooImpl(foo);
    }
    private <T> void setFooImpl(Foo<T> foo) {
        impl = new BarImpl<T>(foo);
    }

    public void startStuff() {
        impl.startStuff();
    }

    public void finishStuff() {
        impl.finishStuff();
    }
}
Run Code Online (Sandbox Code Playgroud)


eri*_*son 4

您的问题与“捕获助手”解决的问题类似,但我不确定它是否可以应用于使用两种单独方法的第二个示例。您的第一个doStuff方法肯定可以更好地编写为public void doStuff(Foo<?> foo),因为无论类型参数如何,它都可以工作Foo。那么,“捕获助手”模式就会很有用。


更新:经过一番修改,扩展了 Goetz 捕获助手的想法,我想出了这个。里面看起来有点乱;从外面看,你不会怀疑任何事情。

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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)