1

我需要将代码发送到另一个方法,因为类中的方法数量可能会改变,我使用反射。但问题是我无法在循环中枚举它们benchmarkMethods.get(i).invoke(set, null);,因为对我来说匿名类可能只能传递最终变量。在这种情况下我该怎么办?

public void RunAllBenchmarks(final Object set, boolean randomOrder) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    final List<Method> benchmarkMethods = new ArrayList<Method >();
    final Class benchClass = set.getClass();
    Method[] methods = benchClass.getDeclaredMethods();
    for (int i = 0; i < methods.length; i++){
        Annotation[] annotations = methods[i].getAnnotations();
        if (annotations.length != 0){
            for (int j = 0; j < annotations.length; j++)
            if (annotations[j].annotationType().toString().contentEquals("interface Benchmark"))
                benchmarkMethods.add(methods[i]);
        }
    }
    for (int i = 0; i < benchmarkMethods.size(); i++){
        String name = null;
        Method method = benchmarkMethods.listIterator().next();
        MeasureAndRecord(name, new IFunc() {
            @Override
            public Object onEvent() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {

                return benchmarkMethods.get(i).invoke(set, null);
            }
        });

    }

    PrintWriter writer = new PrintWriter(System.out);
    PrintResults(writer);
}
4

3 回答 3

1

我想我会补充一点,我使用一个技巧将非最终变量传递给匿名类。取你上面写的一段代码,我做了一些小的改动来展示我是怎么做的。

我假设这里measureAndRecord是一个方法(我已经将第一个字符去掉大写来表示它),这IFunc就是您要为其创建匿名扩展的类。我还假设这Method method ...应该是您要传递给匿名类的方法(因为在您的代码中它不做任何事情)。

这里的技巧是添加一个名为的新方法init,您可以将变量传递给(最终或非最终)并返回this(在这种情况下是匿名类)。发生的情况是调用匿名类,init立即调用方法,然后返回并使用对象(根据需要为 IFunc)。

必须init在构造时(分配之前)调用该方法,并且不能以这种方式将方法链接在一起。原因是一旦对象从第一个方法调用返回,它就是一个 IFunc,而不是您创建的匿名子类,因此 Java 将不再识别之后的覆盖方法之外的任何东西。

不管怎样,玩一玩。我觉得它方便,如果有点作弊的话。即使 Java 8 出现并允许传递几乎最终的变量,这仍然适用于变量变量。

...

for (int i = 0; i < benchmarkMethods.size(); i++) {
    String name = null;
    Method method = benchmarkMethods.get(i); // Assumption on my part

    measureAndRecord(name, new IFunc() {
        // Anonymous class variables matching what we want to pass
        Method method;
        int i;

        // Cheating "Constructor"
        public IFunc init (Method method, int i) {
            this.method = method;
            this.i = i;
            return this; // Must return this for assignment
        }

        @Override
        public Object onEvent() throws InvocationTargetException,    
            IllegalAccessException, NoSuchMethodException {
                return method.invoke(set, null);
            }
        }.init(method, i)); // Note that init is called BEFORE assignment
    }
}

...
于 2014-02-25T11:58:18.527 回答
0

An approach could be to avoid the anonymous inner class. i.e. have a class MethodIFunc implements IFunc that invokes the method on its onEvent() i.e.

for (...) {
    Method method = ...

    // this class calls method.invoke(..) inside its onEvent() method
    MethodIFunc mi = new MethodIFunc(method, set);
    MeasureAndRecord(name, mi);
}

Another approach (btw: you have a double iteration in the second for-loop I assume this is not the actual code) could be to use the for-each statement that allows the variable to be declared final:

for (final Method method : benchmarkMethods) {
    ...
    MeasureAndRecord(name, new IFunc() {
        @Override
        public Object onEvent() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
            return method.invoke(set, (Object[]) null);
        }
    });

}
于 2013-07-29T09:53:08.123 回答
0

您可以使用可变的 int 包装器,例如

final AtomicInteger ai = new AtomicInteger();
for (int i = 0; i < benchmarkMethods.size(); i++){
    String name = null;
    Method method = benchmarkMethods.listIterator().next();
    ai.set(i);
    MeasureAndRecord(name, new IFunc() {
        @Override
        public Object onEvent() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
            return benchmarkMethods.get(ai.get()).invoke(set, null);
        }
    });
}
于 2013-07-29T10:14:24.533 回答