隐藏“本地” Java中的类型参数

发布于 2024-07-16 16:18:49 字数 1077 浏览 1 评论 0原文

假设我使用带有泛型类型参数的接口,

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

目的是类型 T 是抽象的:它对 Foo 的实现强制执行类型约束,但客户端代码并不关心 T 到底是什么。

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

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

但是假设我想分解 doStuff 的工作,在类 Bar 中保存一些状态。 在这种情况下,我似乎需要将 Foo 的类型参数添加到 Bar 中。

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

  /* ... */

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

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

这有点奇怪,因为类型参数 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
  ... 
}

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
  ... 
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

独﹏钓一江月 2024-07-23 16:18:49

为了发挥作用,您有时需要设置 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();
    }
}

To be useful, at some point you are going to set the foo field. At that point you should know (or be able to capture) T. I would suggest doing that in the constructor, and then it would make sense for Bar to have a generic parameter. You could even use an interface so client code doesn't have to see the type. However, I assume you aren't going to take my advice and really want a setFoo. So just add a point to switchable implementation:

/* 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();
    }
}
满意归宿 2024-07-23 16:18:49

您的问题类似于 "capture helper" 解决的问题, 但我不确定它是否可以应用于使用两种单独方法的第二个示例。 您的第一个 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);
    }
  }
}

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);
    }
  }
}
梦里寻她 2024-07-23 16:18:49

为什么不采用三层层次结构:

abstract class Foo

abstract class FooImplBase<T> extends Foo

class Bar extends FooImplBase<String>

客户端只知道 Foo,其中不包含任何通用方法。 在 FooImplBase 中引入您需要的任何泛型方法,然后从中派生具体类。

因此,在您的示例中, startStuff()endStuff()Foo 中是抽象的,并在 FooImplBase 中实现>。 这听起来对你的实际情况有用吗? 我同意这有点麻烦。

Why not have a three-tier hierarchy:

abstract class Foo

abstract class FooImplBase<T> extends Foo

class Bar extends FooImplBase<String>

Clients only know about Foo, which doesn't contain any generic methods. Introduce any generic methods you need in FooImplBase<T> and then the concrete class derives from it.

So in your example startStuff() and endStuff() would be abstract in Foo and implemented in FooImplBase<T>. Does that sound like it might work in your real situation? I agree it's a bit cumbersome.

玩套路吗 2024-07-23 16:18:49

您正在定义 Bar 类。 有两件事是正确的...

1) Bar 中不涉及参数类型。 也就是说, foo 和 t 成员具有单一类型,例如 U,该类型对于定义来说是固定的。 如果您传递了一个 Foo 并希望分配给 foo,那么它必须是 Foo。 如果这一切都是真的,那么是的,它不是公共接口的一部分 - 一切都有特定的类型。 然后,我不确定“量化”是什么意思,因为没有自由类型变量。 如果您的意思是普遍量化,那么您如何协调 Bar 没有参数类型这一事实,因此必须为其每个成员指定一个具体类型?

2)Bar中涉及到参数类型。 它在公共接口中可能并不明显,但也许您传入了 Foo,因此您希望使用多个类型来实例化 Bar 类。 然后,如前所述,这是在公共接口中,您需要使用泛型在 T 中使 Bar 参数化。 这为您提供了某种形式的定义的通用量化,“对于所有类型 T,此定义都是正确的”。

You are defining the Bar class. Two things are true...

1) There is no parametric type involved in Bar. That is, the foo and t members have a single type, say U, that is fixed for the definition. If you're passed a Foo that you expect to assign to foo, it must be a Foo<U>. If this is all true, then yes, it's not part of the public interface - everything has a specific type. Then, I'm not sure what you mean by "quantify", as there are no free type variables. If you mean universally quantify, how do you reconcile the fact that Bar has no parametric typing, so must have been given a concrete type for each of it's members?

2) There is a parametric type involved in Bar. It may not be obviously in the public interface, but perhaps you pass in a Foo<T>, so you want to have a Bar class be instantiated with more than a single type. Then, as stated, this is in the public interface, and you need to make Bar parametric in T with generics. This gives you some form of universal quantification for the definition, "For all types T, this definition is true".

淡水深流 2024-07-23 16:18:49

必须在某个地方决定 Bar 类中的“T”使用什么类型。 因此,要么必须在 Bar 的定义中选择它(在类定义中用 Foo 替换 Foo),要么将其留给 Bar 类的客户端:在这种情况下,Bar 必须是通用的。

如果您希望有一个不依赖于 T 的 Bar 接口并且能够为 T 选择不同类型,则应该使用非泛型接口或抽象基类,如下所示:

interface Bar {
  void startStuff();
  // ...
}

class BarImplement<T> {
  private Foo<T> foo;
  // ...
}

Somewhere it must been decided, what type you want to use for 'T' inside the Bar class. So either you must chose it in the definition of Bar (replacing Foo by Foo inside the class definition) or you leave it up to the client of the Bar class: In this case Bar must be made generic.

If you want to have an interface to Bar not relying on T and be able to chose different types for T, you should use a non-generic interface or an abstract base class, as in:

interface Bar {
  void startStuff();
  // ...
}

class BarImplement<T> {
  private Foo<T> foo;
  // ...
}
寄离 2024-07-23 16:18:49

如果你真的想引起一些愤怒,你可以将 Bar 类放在 Foo 接口中,并以这种方式吸掉 T。 有关接口内类的更多信息,请参阅本文 。 也许这是唯一有意义的情况?

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

  public class Bar<T> {
    private Foo<T> foo;
    private T t;
    public void startStuff() {
      t = foo.getOne();
    }
    public void finishStuff() {
      foo.useOne(t);
    }
  }
}

If you really want to draw some ire, you can put the Bar class inside the Foo interface, and suck the T of that way. See this article for more information about classes inside interfaces. Maybe this is the one case where that makes sense?

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

  public class Bar<T> {
    private Foo<T> foo;
    private T t;
    public void startStuff() {
      t = foo.getOne();
    }
    public void finishStuff() {
      foo.useOne(t);
    }
  }
}
荒芜了季节 2024-07-23 16:18:49

对于 Bar 而言,这种情况下的参数 T 变得毫无用处,因为它将在编译时被擦除为 Object。 所以你也可以“省去麻烦”,尽早进行擦除:

public class Bar {

    private Foo<Object> foo;
    private Object t;

  ... 
}

The parameter T in this case becomes useless as far as Bar is concerned, since it will be erased to Object at compile time. So you could as well "save yourself the trouble", and do the erasure early:

public class Bar {

    private Foo<Object> foo;
    private Object t;

  ... 
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文