16

I have this code:

    List<Runnable> r = new ArrayList<>();
    for(int i = 0; i < 10; i++) {
        r.add(new Runnable() {

            @Override
            public void run() {
                System.out.println(i);
            }
        });
    }

It obviously does not compile because i would need to be final to be used in the anonymous class. But I can't make it final because it is not. What would you do? A solution is to duplicate it but I thought there might be a better way:

    List<Runnable> r = new ArrayList<>();
    for(int i = 0; i < 10; i++) {
        final int i_final = i;
        r.add(new Runnable() {

            @Override
            public void run() {
                System.out.println(i_final);
            }
        });
    }

EDIT just to make it clear, I used a Runnable here for the sake of the example, the question is really about anonymous classes, which could be anything else.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • 5
    I dont think there is a better way... – thumbmunkeys Mar 13 '12 at 11:20
  • 1
    Since a loop counter for obvious reasons can never be made final, I think your approach of copying the value into a final variable is the only way to go (but I'm interested in alternatives I may be missing). – ftr Mar 13 '12 at 11:21

6 Answers6

17

I think your solution is the simplest way.

Another option would be to refactor the creation of the inner class into a factory function that does it for you, then your loop itself could be something clean like:

List<Runnable> r = new ArrayList<>();
for(int i = 0; i < 10; i++) {
    r.add(generateRunnablePrinter(i));
}

And the factory function could just declare a final parameter:

private Runnable generateRunnablePrinter(final int value) {
    return new Runnable() {
       public void run() {
           System.out.println(value);
       }
    };
}

I prefer this refactored approach because it keeps the code cleaner, is relatively self descriptive and also hides away all the inner class plumbing.

Random digression: if you consider anonymous inner classes to be equivalent to closures, then generateRunnablePrinter is effectively a higher order function. Who said you can't do functional programming in Java :-)

mikera
  • 105,238
  • 25
  • 256
  • 415
3

This is what IntelliJ does for you as a fix. The only difference is I would do

ExecutorService es = 
for(int i = 0; i < 10; i++) {
    final int i_final = i;
    es.execute(new Runnable() {
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
2

(Less-than optimal) alternative: create a small inner class that implements Runnable:

class Printer implements Runnable {
    private int index;

    public Printer(int index) {
        this.index = index;
    }

    public void run() {
        System.out.println(index);
    }
}

List<Runnable> r = new ArrayList<>();
for(int i = 0; i < 10; i++) {
    r.add(new Printer(i));
}
Tudor
  • 61,523
  • 12
  • 102
  • 142
  • 1
    @assylias: True, it doesn't. Normally, I would also use your initial version, since the code in the run method is so small... – Tudor Mar 13 '12 at 11:25
  • 1
    @assylias, actually non-anon classes [but declared with a method body] are significantly better in very important cases: class histograms and stack traces. I try to avoid the anonymous ones if I can help it. – bestsss Aug 07 '12 at 16:14
1

I'm afraid there's no other way than copy your counter to a second final variable and use that in your anonymous inner class. This is one of the "deficiencies" of Java around the topic of closures and an advertised advantage of sibling languages like Groovy.

vagelis
  • 334
  • 3
  • 9
  • 1
    It's not really a deficiency: it stops the user from making certain kinds of errors. If the Java compiler didn't enforce such variables as being final, then a lot of people might assume (mistakenly) that they can update a local variable and that the "closure" would see the result. And you can bet that that would cause a lot of bugs and a lot confused SO questions! – mikera Mar 13 '12 at 11:27
1

Looks fine to me. You want to use the value of the loop variable inside your anonymous class and the loop variable obviously can't be final (as its value changes).

Creating a new final local variable is a good solution.

Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268
1

Your solution isn't bad. You could do other stuff, like define your own subclass of Runnable and initialize it with i in the constructor or initialization blocks, but in this case I think it would be just adding complexity without a good cause.

BTW: I assume your example is a syntehetic one, in practice creating a new Runnable just to print an integer wouldn't seem a good idea.

Michał Kosmulski
  • 9,855
  • 1
  • 32
  • 51