隐藏“本地” Java中的类型参数
假设我使用带有泛型类型参数的接口,
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
为了发挥作用,您有时需要设置
foo
字段。 那时您应该知道(或能够捕获)T
。 我建议在构造函数中执行此操作,然后Bar
拥有通用参数就有意义了。 您甚至可以使用接口,这样客户端代码就不必查看类型。 不过,我假设您不会采纳我的建议,并且确实想要一个setFoo
。 所以只需在可切换实现中添加一点即可: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 forBar
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 asetFoo
. So just add a point to switchable implementation:您的问题类似于 "capture helper" 解决的问题, 但我不确定它是否可以应用于使用两种单独方法的第二个示例。 您的第一个
doStuff
方法绝对可以更好地编写为public void doStuff(Foo foo)
,因为无论Foo
类型如何,它都可以工作范围。 那么,“捕获助手”模式就会很有用。更新:经过一番修改,扩展了 Goetz 捕获助手的想法,我想出了这个。 里面看起来有点乱; 从外面看,你不会怀疑任何事情。
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 aspublic void doStuff(Foo<?> foo)
, since it works regardless ofFoo
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.
为什么不采用三层层次结构:
客户端只知道
Foo
,其中不包含任何通用方法。 在FooImplBase
中引入您需要的任何泛型方法,然后从中派生具体类。因此,在您的示例中,
startStuff()
和endStuff()
在Foo
中是抽象的,并在FooImplBase
中实现>。 这听起来对你的实际情况有用吗? 我同意这有点麻烦。Why not have a three-tier hierarchy:
Clients only know about
Foo
, which doesn't contain any generic methods. Introduce any generic methods you need inFooImplBase<T>
and then the concrete class derives from it.So in your example
startStuff()
andendStuff()
would be abstract inFoo
and implemented inFooImplBase<T>
. Does that sound like it might work in your real situation? I agree it's a bit cumbersome.您正在定义 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".
必须在某个地方决定 Bar 类中的“T”使用什么类型。 因此,要么必须在 Bar 的定义中选择它(在类定义中用 Foo 替换 Foo),要么将其留给 Bar 类的客户端:在这种情况下,Bar 必须是通用的。
如果您希望有一个不依赖于 T 的 Bar 接口并且能够为 T 选择不同类型,则应该使用非泛型接口或抽象基类,如下所示:
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:
如果你真的想引起一些愤怒,你可以将 Bar 类放在 Foo 接口中,并以这种方式吸掉 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?
对于 Bar 而言,这种情况下的参数 T 变得毫无用处,因为它将在编译时被擦除为 Object。 所以你也可以“省去麻烦”,尽早进行擦除:
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: