java 如何迭代通配符泛型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6271960/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to iterate over a wildcard generic?
提问by roesslerj
How can I iterate over a wildcard generic? Basically I would like to inline the following method:
如何迭代通配符泛型?基本上我想内联以下方法:
private <T extends Fact> void iterateFacts(FactManager<T> factManager) {
for (T fact : factManager) {
factManager.doSomething(fact);
}
}
If this code is in a separate method as shown, it works because the generic method context allows to define a wildcard type (here T
) over which one can iterate. If one tries to inline this method, the method context is gone and one cannot iterate over a wildcard type anymore. Even doing this automatically in Eclipse fails with the following (uncompilable) code:
如果此代码位于如图所示的单独方法中,则它可以工作,因为泛型方法上下文允许定义T
可以迭代的通配符类型(此处)。如果尝试内联此方法,则方法上下文将消失,并且无法再迭代通配符类型。即使在 Eclipse 中自动执行此操作也会失败并显示以下(不可编译的)代码:
...
for (FactManager<?> factManager : factManagers) {
...
for ( fact : factManager) {
factManager.doSomething(fact);
}
...
}
...
My question is simply: Is there a way to put some wildcard type one can iterate over, or is this a limitation of generics (meaning it is impossible to do so)?
我的问题很简单:有没有办法放置一些可以迭代的通配符类型,或者这是泛型的限制(意味着不可能这样做)?
采纳答案by Pa?lo Ebermann
Type parameters can only defined on
类型参数只能定义在
- types (i.e. classes/interfaces),
- methods, and
- constructors.
- 类型(即类/接口),
- 方法,和
- 构造器。
You would need a type parameter for a local block, which is not possible.
您需要一个本地块的类型参数,这是不可能的。
Yeah, I missed something like this sometimes, too.
是的,我有时也错过了这样的事情。
But there is not really a problem with having the method non-inlined here - if it presents a performance bottleneck where inlining would help, Hotspot will inline it again (not caring about the type).
但是在这里非内联该方法并没有真正的问题 - 如果它出现了内联有帮助的性能瓶颈,Hotspot 将再次内联它(不关心类型)。
Additionally, having a separate method allows giving it a descriptive name.
此外,拥有一个单独的方法允许给它一个描述性的名称。
Just an idea, if you need this often:
只是一个想法,如果你经常需要这个:
interface DoWithFM {
void <T> run(FactManager<T> t);
}
...
for (FactManager<?> factManager : factManagers) {
...
new DoWithFM() { public <T> run(FactManager<T> factManager) {
for (T fact : factManager) {
factManager.doSomething(fact);
}
}.run(factManager);
...
}
...
回答by irreputable
No. In situation like this, the workaround is to create a helper method.
不。在这种情况下,解决方法是创建一个辅助方法。
The JLS has this example http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.10
JLS 有这个例子http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.10
public static void reverse(List<?> list) { rev(list);}
private static <T> void rev(List<T> list) { ... }
The issue is, we have a List<?>
object. We know it must be a List<X>
of some X
, and we'd like to write code using X
. Internally compiler does convert the wildcard to a type variable X
, but Java language does not offer programmers a direct way to access it. But if there's a method accepting List<T>
, we can pass the object to the method. Compiler infers that T=X
and the call is good.
问题是,我们有一个List<?>
对象。我们知道它必须是List<X>
some 中的一个X
,并且我们想使用X
. 内部编译器确实将通配符转换为类型变量X
,但 Java 语言没有为程序员提供直接访问它的方法。但是如果有一个方法 accepting List<T>
,我们可以将对象传递给该方法。编译器推断出这一点T=X
并且调用是好的。
If there's no type erasure, X
can be known at runtime, then Java would definitely give us a way to access X
. However as of today since X
isn't available at runtime, there's not much point. A purely synthetic way could be provided, which is unlikely to be simpler than the helper method workaround.
如果没有类型擦除,X
可以在运行时知道,那么 Java 肯定会给我们一种访问X
. 但是截至今天,由于X
在运行时不可用,因此没有多大意义。可以提供一种纯粹的合成方式,这不太可能比辅助方法变通方法更简单。
回答by Edwin Dalorzo
Well, I can think of a way to do it using inner classes, because the inner class shares the type parameter with its enclosing type. Also, even using wildcards you could still process your collections thanks to wildcard capture conversion.
好吧,我可以想出一种使用内部类的方法,因为内部类与其封闭类型共享类型参数。此外,即使使用通配符,由于通配符捕获转换,您仍然可以处理您的集合。
Let me create an example. This code compiles and runs fine. But I cannot be certain if the use of inner classes would be an issue for you.
让我创建一个例子。此代码编译并运行良好。但是我不能确定使用内部类是否对您来说是个问题。
//as you can see type parameter belongs to the enclosing class
public class FactManager<T> implements Iterable<FactManager<T>.Fact> {
private Collection<Fact> items = new ArrayList<Fact>();
public void doSomething(Fact fact) {
System.out.println(fact.getValue());
}
public void addFact(T value) {
this.items.add(new Fact(value));
}
@Override
public Iterator<Fact> iterator() {
return items.iterator();
}
public class Fact {
//inner class share its enclosing class type parameter
private T value;
public Fact(T value) {
this.value = value;
}
public T getValue() {
return this.value;
}
public void setValue(T value) {
this.value = value;
}
}
public static void main(String[] args) {
List<FactManager<String>> factManagers = new ArrayList<FactManager<String>>();
factManagers.add(new FactManager<String>());
factManagers.get(0).addFact("Obi-wan");
factManagers.get(0).addFact("Skywalker");
for(FactManager<? extends CharSequence> factManager : factManagers){
//process thanks to wildcard capture conversion
procesFactManager(factManager);
}
}
//Wildcard capture conversion can be used to process wildcard-based collections
public static <T> void procesFactManager(FactManager<T> factManager){
for(FactManager<T>.Fact fact : factManager){
factManager.doSomething(fact);
}
}
}
回答by Alexander Pogrebnyak
You can always fall back to Object
你总是可以回到 Object
for (FactManager<?> factManager : factManagers) {
...
for ( Object fact : factManager) {
factManager.doSomething(fact);
}
...
}
This, of course, is subject to what is the actual declaration of doSomething
.
当然,这取决于doSomething
.
If doSomething
is declared as this void doSomething( T fact )
, then your recourse here would be to use a raw type and swallow unchecked
warnings. If you can guarantee that FactManager
can only have homogeneous Facts
inserted, then that may be an OK solution.
如果doSomething
声明为 this void doSomething( T fact )
,那么您在这里可以使用原始类型并吞下unchecked
警告。如果你能保证FactManager
只能有同构的Facts
插入,那么那可能是一个OK的解决方案。
for (FactManager factManager : factManagers) { // unchecked warning on this line
...
for ( Object fact : factManager) {
factManager.doSomething(fact);
}
...
}
回答by ig0774
This is more precisely matched to the method you defined (that is, if you can call iterateFacts() with the FactManagers in factManagers, you know that the FactManager contain items that are some subclass of Fact).
这与您定义的方法更精确匹配(也就是说,如果您可以使用 factManagers 中的 FactManager 调用 iterateFacts(),则您知道 FactManager 包含属于 Fact 某些子类的项目)。
for (FactManager<? extends Fact> factManager : factManagers) {
for (Fact fact : factManager) {
factManager.doSomething(fact);
}
}
I would tend to think, however, that you would declare FactManager to be generic for subtypes of Fact (just given the name of the class), e.g.
但是,我倾向于认为,您会将 FactManager 声明为 Fact 子类型的泛型(仅给出类的名称),例如
class FactManager<T extends Fact> implements Iterable<T> {
...
}
The Eclipse refactoring fails because it cannot infer the type of an object contained by FactManager<?>
.
Eclipse 重构失败,因为它无法推断FactManager<?>
.