0

This is copied from JPL (see comment below), I added import and 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());
    }
}

My questions are:

  1. When iter.hasNext() is called, how iter can know what objs means? It was not explicitly saved in the instance. From discussion method-local inner class cannot use variables declared within the method it looks like it was implicitly copied and saved in iter instance. Could you confirm and substantiate it? I failed to find a reference.

  2. If the first is true (the final parameter was saved), is it considered a good programming practice to rely on such implicit saving? Sorry if it is in the Java specifications, then my second Q is irrelevant, but again - I did not find it.

The result is 5 null's, I left the elements of the array uninitialized.

Community
  • 1
  • 1
TT_ stands with Russia
  • 1,785
  • 3
  • 21
  • 33

2 Answers2

2

Following is what's produced by the command

javap -private -c LocalInnerClassAppl\$1Iter

-

pkg is the package where I copied your class to compile it.

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

}

As you can see the field private final java.lang.Object[] val$objs; and the constructor LocalInnerClassAppl$1Iter(java.lang.Object[]) generated by the compiler. I think that pretty much answers the first question.

For the second question it really depends on whether you need to instantiate the class multiple times, or do you just need to instantiate it once and you are done with it, depending on that you that you should decide whether or not to make it local/anonymous. If it fits your situation best, then it most probably is the good practice.

Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
  • Thanks, so you confirm the first one. Any references? The second q. was not about local class, it was about relying on compiler behavior for final parameter. – TT_ stands with Russia Jul 02 '13 at 13:47
  • Sorry, I couldn't find any reference. *is it considered a good programming practice to rely on such implicit saving?* IMO, this is just one of many things that compiler performs to fulfill the language features. If we want to use some feature than we must rely on what compiler does to under the hood to provide that feature. Also as far as I understand the JLS is about the Java language not the compiler. – Bhesh Gurung Jul 02 '13 at 14:54
  • Precisely, that's my point: if this is a documented language feature, then we can assume that any Java implementation does the same. If this is compiler specific behavior, then we better copy the parameters "manually" to be sure. – TT_ stands with Russia Jul 02 '13 at 16:47
  • 1
    [This](http://stackoverflow.com/q/4732544/738746) question has answers that explains this a lot better. [Here](http://docs.oracle.com/javase/specs/jls/se5.0/html/classes.html#8.1.3) is the link to the JLS section which covers this. This is what is states about local classes and local variables - *Any local variable, formal method parameter or exception handler parameter used but not declared in an inner class must be declared final.*. – Bhesh Gurung Jul 02 '13 at 17:09
  • Thanks for the first link, it's interesting. As for the 2-nd one, I don't see how it is related to my question: yes, they must be final, so what? Anyway, the 1-st question seems to be answered. As for the 2-nd, I'll better be using explicit parameter copy in my code. – TT_ stands with Russia Jul 02 '13 at 18:12
  • I would consider that only if the class wasn't local, i.e. only if I needed to instantiate it multiple times, with each instance taking different `Object[]`s. In your case it's local, the definition is use once and throw away, so you would just re-implementing whatever the compiler implicitly provides (considering that `objs` can be final). And if it wasn't final then the compiler itself wouldn't let you use it within the class. It's made that way by the experts, it's been there for years, and it's been proven guaranteed to work. Now I am not really sure what else would you need to rely on it. – Bhesh Gurung Jul 02 '13 at 18:40
1

Since objs is a final variable in an enclosing scope of your anonymous class, it is "captured" by the class and its lifetime is at least as long as the class. The spec isn't all that clear, but its implied by this line:

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

In general, any scope inherits its parent scope; an inner class is slightly different in that it only inherits the final members of its parent local scope, and all of its enclosing class's scope.

This is a common pattern for factory methods, especially when dealing with asynchronous code that involves listeners that need the enclosing state.

Roland
  • 7,525
  • 13
  • 61
  • 124
Haldean Brown
  • 12,411
  • 5
  • 43
  • 58