Capturing counter around switch inside anonymous classes inside some cases?

I was trying to write a switch inside a loop, where inside 2/5 of the cases, an anonymous class is made, which captures the loop counter. It’s not straight forward because the counter needs to be final to be able to be captured by the anonymous inner class. The solution is simple though, just make a final int i_ which gets set to the counter variable. The problem is that it doesn’t work (I guess because there’s more than one case). Here is an extremely simplified piece of code that has the same problem as in my real code:

import java.util.concurrent.*;
import java.util.*;
enum E {A,B,C,D,E}
class A {
    static void s() {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    static Semaphore s = new Semaphore(2); // 2 cores
    public static void main(String[] _){
        LinkedList<E> es = new LinkedList<E>();
        es.push(E.A);
        es.push(E.D);
        es.push(E.B);
        es.push(E.C);
        es.push(E.E);
        es.push(E.C);
        es.push(E.C);
        es.push(E.E);
        es.push(E.A);
        es.push(E.A);
        f(es);
    }
    static void f(List<E> es) {
        int i = 0;
        for (E e : es) {
            s.acquireUninterruptibly();
            switch(e) {
            case A:
                final int i_ = i;
                new Thread() {
                    public void run() {
                        System.out.println("A" + i_); s(); s.release();
                    }
                }.start();
                break;
            case B:
                final int i_ = i;
                new Thread() {
                    public void run() {
                        System.out.println("B" + i_); s(); s.release();
                    }
                }.start();
                break;
            case C:
                new Thread() {
                    public void run() {
                        System.out.println("C"); s(); s.release();
                    }
                }.start();
                break;
            case D:
                new Thread() {
                    public void run() {
                        System.out.println("D"); s(); s.release();
                    }
                }.start();
                break;
            case E:
                new Thread() {
                    public void run() {
                        System.out.println("E"); s(); s.release();
                    }
                }.start();
                break;
            default:
                break;
            }
            i++;
        }
    }
}

It spawns threads to do work. Which work to do is decided by the current element from the input list es. A semaphore is used to bound the number of currently running threads.

It fails to compile, claiming that i is already defined:

$ javac A.java && java A
A.java:27: i_ is already defined in main(java.lang.String[])
                final int i_ = i;
                          ^
1 error

But they are defined in different cases of the switch. I assumed it would work since you can do the same with any other type of block, for example this works:

class A {
    static {
        for (int i = 0; i < 1; i++) {
            int j = i + 1;
        }
        for (int i = 0; i < 1; i++) {
            int j = i + 1;
        }
    }
}

Why doesn’t it work with the switch? What other ways are there to capture a counter around the switch inside anonymous classes inside multiple cases?

Answer

Add braces:

case A: {
    ...
}
case B: {
    ...
}
...

As you have it, all i_ declarations are in the same scope, which results in a compilation error. Two pieces of code that are nested at the same level within {} braces are always in the same scope, so the fact that the declarations are in different cases makes no difference.

Leave a Reply

Your email address will not be published. Required fields are marked *