How to iterate over a common template?

How can I iterate over a common template? Basically, I would like to embed 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 general context of the method allows you to determine the type of wildcard (here T ) by which iteration can be performed. If you try to embed this method, the method context disappears, and you can no longer iterate over the type of wildcard. Even so, the following (incompatible) code fails to execute automatically in Eclipse:

 ... for (FactManager<?> factManager : factManagers) { ... for ( fact : factManager) { factManager.doSomething(fact); } ... } ... 

My question is simple: is there a way to put some type of wildcard that can be sorted out, or is this a generic restriction (which means it's impossible)?

+8
java generics foreach
source share
5 answers

Type parameters can only be defined on

  • (i.e. classes / interfaces),
  • and
  • Constructors.

You need a type parameter for a local block, which is not possible.

Yes, I sometimes missed something like that.

But there really is no problem with the fact that the method is not nested in it - if it is a performance bottleneck in which inlineation will help, Hotspot will turn it on again (without worrying about type).

In addition, the presence of a separate method allows you to give it a descriptive name.


Just an idea if you need it 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); ... } ... 
+3
source share

Not. In such a situation, a workaround is to create a helper method.

JLS has this example 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 problem is that we have a List<?> Object. We know that this must be a List<X> for some X , and we would like to write code using X The internal compiler converts the wildcard to a variable of type X , but the Java language does not offer programmers direct access to it. But if there is a method that takes List<T> , we can pass the object to the method. The compiler reports that T=X and the call is good.

If there is no type erasure, X may be known at run time, then Java will definitely give us access to X However, to date, since X not available at run time, there is not much point. A purely synthetic method can be provided, which is unlikely to be simpler than a bypass method for the helper method.

+5
source share

You can always go back to Object

 for (FactManager<?> factManager : factManagers) { ... for ( Object fact : factManager) { factManager.doSomething(fact); } ... } 

This of course obeys what is the actual doSomething .

If doSomething declared like this void doSomething( T fact ) , then your regression here would be to use raw types and swallow unchecked warnings. If you can guarantee that FactManager can only be inserted homogeneous Facts , then this could be an OK solution.

 for (FactManager factManager : factManagers) { // unchecked warning on this line ... for ( Object fact : factManager) { factManager.doSomething(fact); } ... } 
+2
source share

Well, I can come up with a way to do this using inner classes, because the inner class shares the type parameter with its encompassing type. In addition, even using wildcards, you can still process your collections by converting wildcards.

Let me create an example. This code compiles and works fine. But I cannot be sure that using inner classes will be a problem 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); } } } 
+2
source share

This more closely matches the method you define (that is, if you can call iterateFacts () with FactManagers in factManagers, you know that FactManager contains elements that are some subclass of Fact).

 for (FactManager<? extends Fact> factManager : factManagers) { for (Fact fact : factManager) { factManager.doSomething(fact); } } 

I would be inclined to think, however, that you would declare FactManager universal for fact subtypes (only with class name), for example

 class FactManager<T extends Fact> implements Iterable<T> { ... } 

Eclipse refactoring fails because it cannot infer the type of the object contained in FactManager<?> .

0
source share

All Articles