最终参数被复制到隐藏变量中?

时间:2013-07-02 03:46:31

标签: java parameters local final

这是从JPL复制的(见下面的评论),我添加了import和main():

import java.util.*;

/**
 * @From "The Java Programming Language" by Arnold, Gosling, Holmes
 * (5.3. Local Inner Classes)
 */

public class LocalInnerClassAppl {

    public static Iterator<Object> walkThrough(final Object[] objs) {

        class Iter implements Iterator<Object> {

            private int pos = 0;

            @Override
            public boolean hasNext() {
                return (pos < objs.length);
            }

            @Override
            public Object next() throws NoSuchElementException {
                if (pos >= objs.length)
                    throw new NoSuchElementException();
                return objs[pos++];
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        }

        return new Iter();
    }

    public static void main(String[] args) {
        Object[] objects = new Object[5];
        Iterator<Object> iter = walkThrough(objects);
        while (iter.hasNext())
            System.out.println(iter.next());
    }
}

我的问题是:

  1. 当调用iter.hasNext()时,怎么知道objs意味着什么?它未在实例中明确保存。从讨论method-local inner class cannot use variables declared within the method看来,它被隐式复制并保存在iter实例中。你能证实并证实吗?我找不到参考资料。

  2. 如果第一个为真(保存了最终参数),那么依赖这种隐式保存是否是一种很好的编程习惯?对不起,如果它是在Java规范中,那么我的第二个Q是无关紧要的,但是再次 - 我没有找到它。

  3. 结果是5个null,我将数组的元素保留为未初始化。

2 个答案:

答案 0 :(得分:2)

以下是命令

产生的内容
javap -private -c LocalInnerClassAppl\$1Iter

-

pkg是我复制你的类来编译它的包。

Compiled from "LocalInnerClassAppl.java"
class pkg.LocalInnerClassAppl$1Iter extends java.lang.Object implements java.util.Iterator{
private int pos;

private final java.lang.Object[] val$objs;

pkg.LocalInnerClassAppl$1Iter(java.lang.Object[]);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #14; //Field val$objs:[Ljava/lang/Object;
   5:   aload_0
   6:   invokespecial   #16; //Method java/lang/Object."<init>":()V
   9:   aload_0
   10:  iconst_0
   11:  putfield    #19; //Field pos:I
   14:  return

public boolean hasNext();
  Code:
   0:   aload_0
   1:   getfield    #19; //Field pos:I
   4:   aload_0
   5:   getfield    #14; //Field val$objs:[Ljava/lang/Object;
   8:   arraylength
   9:   if_icmpge   14
   12:  iconst_1
   13:  ireturn
   14:  iconst_0
   15:  ireturn

public java.lang.Object next()   throws java.util.NoSuchElementException;
  Code:
   0:   aload_0
   1:   getfield    #19; //Field pos:I
   4:   aload_0
   5:   getfield    #14; //Field val$objs:[Ljava/lang/Object;
   8:   arraylength
   9:   if_icmplt   20
   12:  new #31; //class java/util/NoSuchElementException
   15:  dup
   16:  invokespecial   #33; //Method java/util/NoSuchElementException."<init>":()V
   19:  athrow
   20:  aload_0
   21:  getfield    #14; //Field val$objs:[Ljava/lang/Object;
   24:  aload_0
   25:  dup
   26:  getfield    #19; //Field pos:I
   29:  dup_x1
   30:  iconst_1
   31:  iadd
   32:  putfield    #19; //Field pos:I
   35:  aaload
   36:  areturn

public void remove();
  Code:
   0:   new #35; //class java/lang/UnsupportedOperationException
   3:   dup
   4:   invokespecial   #37; //Method java/lang/UnsupportedOperationException."<init>":()V
   7:   athrow

}

正如您可以看到编译器生成的字段private final java.lang.Object[] val$objs;和构造函数LocalInnerClassAppl$1Iter(java.lang.Object[])。我认为这几乎是第一个问题的答案。

对于第二个问题,它实际上取决于你是否需要多次实例化该类,或者你只是需要实例化它一次并完成它,这取决于你应该决定是否要制作它它本地/匿名。如果它最适合你的情况,那么它很可能是一种很好的做法。

答案 1 :(得分:1)

由于objs是匿名类的封闭范围中的最终变量,因此它被类“捕获”,其生命周期至少与类一样长。规范并不是那么清楚,但是这条线暗示了它:

  

Any local variable, formal parameter, or exception parameter used but not declared in an inner class must be declared final.

通常,任何范围都会继承其父范围;内部类略有不同,因为它只继承其父本地范围的最终成员及其所有封闭类的范围。

这是工厂方法的常见模式,尤其是在处理涉及需要封闭状态的侦听器的异步代码时。