在某些情况下捕获匿名类内部的开关周围的计数器?

时间:2013-09-16 22:47:15

标签: java variables switch-statement anonymous-class shadowing

我试图在循环中编写一个开关,其中2/5的情况下,创建了一个匿名类,它捕获循环计数器。这不是直截了当的,因为计数器需要是最终的才能被匿名内部类捕获。解决方案很简单,只需将final int i_设置为计数器变量即可。问题是它不起作用(我想因为有不止一个案例)。这是一段极其简化的代码,与我的实际代码中的问题相同:

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++;
        }
    }
}

它产生线程来完成工作。要做的工作取决于输入列表es中的当前元素。信号量用于绑定当前运行的线程数。

无法编译,声称i已经定义:

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

但它们是在交换机的不同情况下定义的。我认为它可以工作,因为你可以对任何其他类型的块做同样的事情,例如这有效:

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;
        }
    }
}

为什么它不适用于交换机?在多个案例中,有哪些其他方法可以在匿名类中围绕交换机捕获一个计数器?

2 个答案:

答案 0 :(得分:3)

添加大括号:

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

如上所述,所有i_声明都在同一范围内,这会导致编译错误。在{}括号内嵌套在同一级别的两段代码总是在同一范围内,因此声明在不同case s中的事实没有区别。

答案 1 :(得分:1)

只需用列表压缩索引即可。这是几十年前ML等人的一个常见习语。我认为这种方法比arshajii更清洁,因为它也消除了迷路i++。此外,大多数Java用户不熟悉案例周围的普通块(然后再次看到zip可能会感到惊讶)。如果您使用包含zip的库,我的代码会更短。或者它应该在标准库中。我将一个实现附加到了你的底层。

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) {
        for (final Pair<Integer,E> p : zip(naturals, es)) {
            s.acquireUninterruptibly();
            switch(p.y) {
            case A:
                new Thread() {
                    public void run() {
                        System.out.println("A" + p.x); s(); s.release();
                    }
                }.start();
                break;
            case B:
                new Thread() {
                    public void run() {
                        System.out.println("B" + p.x); 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;
            }
        }
    }

    // an infinite iterable over all natural numbers
    static Iterable<Integer> naturals = new Iterable<Integer>() {
        public Iterator<Integer> iterator() {
            return new Iterator<Integer>() {
                private int i = 0;
                public void remove() {
                    throw new UnsupportedOperationException();
                }
                public Integer next() {
                    return i++;
                }
                public boolean hasNext() {
                    return true;
                }
            };
        }
    };

    // combine two iterators
    static <X,Y> Iterable<Pair<X,Y>> zip(final Iterable<X> i1,
                                         final Iterable<Y> i2) {
        return new Iterable<Pair<X,Y>>() {
            public Iterator<Pair<X,Y>> iterator() {
                return new Iterator<Pair<X,Y>>() {
                    private final Iterator<X> ix = i1.iterator();
                    private final Iterator<Y> iy = i2.iterator();
                    public void remove() {
                        ix.remove();
                        iy.remove();
                    }
                    public Pair<X,Y> next() {
                        Pair<X,Y> p = new Pair<X,Y>();
                        p.x = ix.next();
                        p.y = iy.next();
                        return p;
                    }
                    public boolean hasNext() {
                        return ix.hasNext() && iy.hasNext();
                    }
                };
            }
        };
    }
}

class Pair<X,Y> {
    X x;
    Y y;
}

javac A.java && java A
A0
A1
E
C
C
E
C
B7
D
A9