Search code examples
javainner-classesanonymous-class

Java: accessing a non-static field in an anonymous class in a static context


Consider the following class:

public class DataStructure {
    
    // Create an array
    private final static int SIZE = 15;
    private int[] arrayOfInts = new int[SIZE];

    public DataStructure() {
        // fill the array with ascending integer values
        for (int i = 0; i < SIZE; i++) {
            arrayOfInts[i] = i;
        }
    }

    public void print(DataStructureIterator iter) {
        while(iter.hasNext()) {
            System.out.print(iter.next() + " ");
        }
        System.out.println();
    }
    
    interface DataStructureIterator extends java.util.Iterator<Integer> { } 

    // Inner class implements the DataStructureIterator interface,
    // which extends the Iterator<Integer> interface
    
    private class EvenIterator implements DataStructureIterator {
        
        // Start stepping through the array from the beginning
        private int nextIndex = 0;
        
        public boolean hasNext() {
            
            // Check if the current element is the last in the array
            return (nextIndex <= SIZE - 1);
        }        
        
        public Integer next() {
            
            // Record a value of an even index of the array
            Integer retValue = Integer.valueOf(arrayOfInts[nextIndex]);
            
            // Get the next even element
            nextIndex += 2;
            return retValue;
        }

    }
    
    public static void main(String s[]) {
        
        // Fill the array with integer values and print out only
        // values of even indices
        DataStructure ds = new DataStructure();
    
        ds.print(
            new DataStructure.DataStructureIterator() {
                private int nextIndex = 1;
                public boolean hasNext() { 
                    return (nextIndex <= ds.size() - 1);
                }
                public Integer next() {
                    //int retValue = arrayOfInts[nextIndex]; // this won't work:
                    // non-static variable arrayOfInts cannot be referenced from a static context
                    int retValue = ds.get(nextIndex);
                    nextIndex += 2;
                    return retValue;
                }
            }
        );        
    }
}

Compiling this, I get:

error: non-static variable arrayOfInts cannot be referenced from a static context

The compiler complains about the access to the arrayOfInts variable from the anonymous class that's in the main method.

My question is: Ok I understand that I can't access a non-static variable from a static context - it completely makes sense. But in this case, I'm not actually accessing the variable from the static context. Rather, I'm passing a piece of code - an anonymous class - to a non-static method. The non-static method can definitely access a non-static variable, so why isn't it possible?


Solution

  • Rather, I'm passing a piece of code - an anonymous class - to a non-static method

    Java doesn't consider what you are doing with the instance that you are creating, to determine whether you can access an non-static field.

    For all Java knows, you could be assigning the instance of the anonymous class to a local variable:

    public static void main(String[] args) {
        var foo = new DataStructure.DataStructureIterator() {
            public boolean hasNext() { 
                ...
            }
            public Integer next() {
                int retValue = arrayOfInts[nextIndex]; // this won't work:
                ...
            }
        };
        foo.next();
    }
    

    What would foo.next() do in this case? There is no instances of DataStructure anywhere!

    That said, there is an instance of DataStructure in your code - ds. You probably meant to access the arrayOfInts in that instance, so just do ds.arrayofInts instead.

    Note that the use of arrayOfInts inside EvenIterator is valid because EvenIterator is a inner class of DataStructure. Note that DataStructureIterator, which is also nested in DataStructure, is not an "inner interface". There is no such thing as "inner interfaces". DataStructure.DataStructureIterator is just another regular type.

    To create an instance of EvenIterator, you need an instance of DataStructure first. This instance of DataStructure is called the enclosing instance of EvenIterator.

    Because EvenIterator is accessing the arrayOfInts of its enclosing instance, and not whatever instance you are calling print on, this can lead to some unexpected results:

    DataStructure ds1 = new DataStructure();
    DataStructure ds2 = new DataStructure();
    EvenIterator iter = ds1.new EvenIterator(); // enclosing instance is ds1
    ds2.print(iter); // prints the elements of ds1, not ds2!