Java: анонимный внутренний класс с использованием локальной переменной - PullRequest
9 голосов
/ 15 июля 2010

Как я могу получить значение userId, переданное этому методу в моем анонимном внутреннем подклассе здесь?

public void doStuff(String userID) {
    doOtherStuff(userID, new SuccessDelegate() {
        @Override
        public void onSuccess() {
            Log.e(TAG, "Called delegate!!!! "+ userID);
        }
    });
}

Я получаю эту ошибку:

Невозможно обратиться к неконечной переменной userID внутри внутреннего класса, определенного в другом методе

Я почти уверен, что не могу присвоить его как final, поскольку это переменная с неизвестным значением. Я слышал, что этот синтаксис каким-то образом сохраняет область видимости, поэтому я думаю, что должен быть синтаксический трюк, которого я пока не совсем знаю.

Ответы [ 5 ]

11 голосов
/ 15 июля 2010

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

Вот (в основном), почему это так ... если вы пишете следующий код (длинный ответ, но, внизу, вы можете получить короткую версию: -):

class Main
{
    private static interface Foo
    {
        void bar();
    }

    public static void main(String[] args)
    {
        final int x;
        Foo foo;

        x = 42;
        foo = new Foo()
        {
            public void bar()
            {
                System.out.println(x);
            }
        };

        foo.bar();
    }
}

компилятор переводит это примерно так:

class Main
{
    private static interface Foo
    {
        void bar();
    }

    public static void main(String[] args)
    {
        final int x;
        Foo foo;

        x = 42;

        class $1
            implements Foo
        {
            public void bar()
            {
                System.out.println(x);
            }
        }

        foo = new $1();
        foo.bar();
    }
}

, а затем это:

class Main
{
    private static interface Foo
    {
        void bar();
    }

    public static void main(String[] args)
    {
        final int x;
        Foo foo;

        x = 42;
        foo = new $1(x);
        foo.bar();
    }

    private static class $1
        implements Foo
    {
        private final int x;

        $1(int val)
        {
           x = val;
        }

        public void bar()
        {
            System.out.println(x);
        }
    }
}

и, наконец, вот что:

class Main
{
    public static void main(String[] args) 
    {
        final int x;
        Main$Foo foo;

        x = 42;
        foo = new Main$1(x);
        foo.bar();
    }
}

interface Main$Foo
{
    void bar();
}

class Main$1
    implements Main$Foo
{
    private final int x;

    Main$1(int val)
    {
       x = val;
    }

    public void bar()
    {
        System.out.println(x);
    }
}

Важным является то, где он добавляет конструктор к $ 1.Представьте себе, если бы вы могли сделать это:

class Main
{
    private static interface Foo
    {
        void bar();
    }

    public static void main(String[] args)
    {
        int x;
        Foo foo;

        x = 42;
        foo = new Foo()
        {
            public void bar()
            {
                System.out.println(x);
            }
        };

        x = 1;

        foo.bar();
    }
}

Можно ожидать, что foo.bar () выведет 1, но на самом деле выведет 42. Требуя, чтобы локальные переменные были окончательными, эта запутанная ситуация не может возникнуть.

7 голосов
/ 15 июля 2010

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

public void doStuff(final String userID) {
   ...

Я не уверен, что вы имели в виду, что это переменная с неизвестным значением; все, что в конечном итоге означает, что после того, как значение присвоено переменной, оно не может быть re . Поскольку вы не изменяете значение userID в своем методе, в этом случае нет проблем сделать его окончательным.

1 голос
/ 19 января 2015

В Java 8 это немного изменилось.Теперь вы можете получить доступ к переменным, которые фактически являются окончательными .Соответствующий фрагмент и пример из документации Oracle (выделено мной):

Однако, начиная с Java SE 8, локальный класс может обращаться к локальным переменным и параметрам включающего блока, которыеявляются окончательными или эффективно окончательными . Переменная или параметр, значение которых никогда не изменяется после инициализации, фактически является окончательным .Например, предположим, что переменная numberLength не объявлена ​​окончательной, и вы добавили выделенный оператор присваивания в конструкторе PhoneNumber:

PhoneNumber(String phoneNumber) {
    numberLength = 7; // From Kobit: this would be the highlighted line
    String currentNumber = phoneNumber.replaceAll(
        regularExpression, "");
    if (currentNumber.length() == numberLength)
        formattedPhoneNumber = currentNumber;
    else
        formattedPhoneNumber = null;
}

Из-за этого оператора присваивания переменная numberLength имеет видне эффективно окончательно.В результате компилятор Java генерирует сообщение об ошибке, похожее на «локальные переменные, на которые ссылается внутренний класс, должны быть окончательными или эффективно окончательными», когда внутренний класс PhoneNumber пытается получить доступ к переменной numberLength:

if (currentNumber.length() == numberLength)

Начиная с Java SE 8, если вы объявляете локальный класс в методе, он может получить доступ к параметрам метода.Например, вы можете определить следующий метод в локальном классе PhoneNumber:

public void printOriginalNumbers() {
    System.out.println("Original numbers are " + phoneNumber1 +
        " and " + phoneNumber2);
}

Метод printOriginalNumbers обращается к параметрам phoneNumber1 и phoneNumber2 метода validatePhoneNumber

1 голос
/ 15 июля 2010

объявите метод

public void doStuff(final String userID)

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

Значение не изменяется в вашем коде, поэтому это безопасное изменение.

1 голос
/ 15 июля 2010

В чем проблема с этим final как в

public void doStuff (final String userID)
...