Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to iterate over a wildcard generic?

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:

...
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)?

like image 310
roesslerj Avatar asked Jun 07 '11 21:06

roesslerj


2 Answers

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

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.

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.

like image 50
irreputable Avatar answered Oct 10 '22 07:10

irreputable


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

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);
    ...
}
...
like image 25
Paŭlo Ebermann Avatar answered Oct 10 '22 06:10

Paŭlo Ebermann