Java: доступ к нестатическому полю в анонимном классе в статическом контексте

Рассмотрим следующий класс:

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

Собрав это, я получаю:

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

Компилятор жалуется на доступ к переменной arrayOfInts из анонимного класса, находящегося в основном методе.

Мой вопрос: хорошо, я понимаю, что я не могу получить доступ к нестатической переменной из статического контекста - это полностью имеет смысл. Но в этом случае я фактически не получаю доступ к переменной из статического контекста. Скорее я передаю часть кода — анонимный класс — нестатическому методу. Нестатический метод определенно может получить доступ к нестатической переменной, так почему же это невозможно?

new DataStructure.DataStructureIterator() должно быть ds.new DataStructure.DataStructureIterator() обязательно?
user207421 07.07.2023 09:49

@user207421 user207421 Почему это должно помочь? Анонимный класс, созданный в статическом контексте, не является внутренним классом, не может иметь вмещающий экземпляр.

Sweeper 07.07.2023 10:49

Итак, следующий вопрос: почему он вообще определяет итератор в основном методе? А если надо, то почему бы не продлить EvenIterator? Что *требует*` контекст ds. и предоставляет нестатический контекст, который ему нужен. В данный момент он получает доступ к вещам, которые находятся вне (нестатической) области видимости.

user207421 07.07.2023 11:34

@ user207421 Да, я согласен, что дизайн кода довольно странный. Я бы предположил, что это всего лишь один из тех надуманных сценариев, демонстрирующих замешательство ОП.

Sweeper 07.07.2023 15:21
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
4
58
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы создаете анонимный класс из статического метода и пытаетесь передать нестатическое поле в реализацию, поэтому вы получаете ошибку компиляции. Принимая во внимание, что класс EvenIterator является внутренним по отношению к DataStructure, поэтому он подразумевает привязку к экземпляру класса DataStructure и может получить доступ к его нестатическим переменным.

Спецификация внутренних и вложенных классов

Как и в случае с методами и переменными экземпляра, внутренний класс связан с экземпляром окружающего его класса и имеет прямой доступ к методам и полям этого объекта.

Ответ принят как подходящий

Скорее я передаю кусок кода - анонимный класс - нестатическому методу

Java не учитывает, что вы делаете с создаваемым экземпляром, чтобы определить, можете ли вы получить доступ к нестатическому полю.

Насколько известно Java, вы можете присвоить экземпляр анонимного класса локальной переменной:

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

Что бы foo.next() сделал в этом случае? Нигде нет экземпляров DataStructure!

Тем не менее, в вашем коде есть экземпляр DataStructureds. Вы, вероятно, имели в виду доступ к arrayOfInts в этом случае, поэтому просто сделайте ds.arrayofInts вместо этого.

Обратите внимание, что использование arrayOfInts внутри EvenIterator допустимо, потому что EvenIterator является внутренним классом DataStructure. Обратите внимание, что DataStructureIterator, который также вложен в DataStructure, не является «внутренним интерфейсом». Нет такого понятия, как «внутренние интерфейсы». DataStructure.DataStructureIterator — это еще один обычный тип.

Чтобы создать экземпляр EvenIterator, вам сначала нужен экземпляр DataStructure. Этот экземпляр DataStructure называется объемлющим экземпляром EvenIterator.

Поскольку EvenIterator обращается к arrayOfInts окружающего его экземпляра, а не к тому экземпляру, к которому вы вызываете print, это может привести к неожиданным результатам:

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!

Что ж, это НАСТОЛЬКО функция, которую я ненавижу, хорошо набранные игроки делают некоторые макияжи вокруг оригинальных авторов и крадут одобрения и голоса. Проверьте историю редактирования ответа

Maksym Kosenko 07.07.2023 15:07

С другой стороны, @YoavKlein, почему ты принял этот ответ, а не мой? Вы друзья? Поддельный аккаунт @Sweeper? Или, может быть, мой ответ не так хорош, и не стоит даже положительного голосования, кроме как хотя бы принять?

Maksym Kosenko 07.07.2023 15:12

Другие вопросы по теме