简体   繁体   English

Java:使用局部变量的匿名内部类

[英]Java: Anonymous inner class using a local variable

How can I get the value of userId passed to this method in my anonymous inner subclass here?如何在此处的匿名内部子类中获取传递给此方法的userId值?

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

I get this error:我收到此错误:

Cannot refer to a non-final variable userID inside an inner class defined in a different method不能在不同方法中定义的内部类中引用非最终变量 userID

I'm pretty sure I can't assign it as final since it's a variable with an unknown value.我很确定我不能将它分配为 final,因为它是一个具有未知值的变量。 I had heard that this syntax does preserve scope in some way, so I think there must be a syntax trick I don't quite know yet.我听说这种语法确实以某种方式保留了作用域,所以我认为一定有一个我还不太了解的语法技巧。

As everyone else here has said, local variables have to be final to be accessed by an inner class.正如这里的其他人所说,局部变量必须是 final 才能被内部类访问。

Here is (basically) why that is... if you write the following code (long answer, but, at the bottom, you can get the short version :-):这就是(基本上)为什么会这样......如果你编写以下代码(长答案,但是,在底部,你可以获得简短版本:-):

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

the compiler translates it roughly like this:编译器大致这样翻译:

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

and then this:然后这个:

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

and finally to this:最后到这个:

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

The important one is where it adds the constructor to $1.重要的是它将构造函数添加到 $1。 Imagine if you could do this:想象一下,如果你可以这样做:

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

You would expect that foo.bar() would print out 1 but it would actually print out 42. By requiring local variables to be final this confusing situation cannot arise.您会期望 foo.bar() 会打印出 1 但它实际上会打印出 42。通过要求局部变量是 final 不会出现这种令人困惑的情况。

Sure you can assign it as final - just put that keyword in the declaration of the parameter:当然你可以将它指定为 final - 只需将该关键字放在参数的声明中:

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

I'm not sure what you meant about it being a variable with an unknown value;我不确定你说它是一个未知值的变量是什么意思; all that final means is that once a value is assigned to the variable, it cannot be re -assigned.最后的意思是,一旦将值分配给变量,就不能重新分配。 Since you're not changing the value of the userID within your method, there's no problem making it final in this case.由于您没有在方法中更改 userID 的值,因此在这种情况下将其设为 final 没有问题。

In Java 8, this has changed a little bit.在 Java 8 中,这已经发生了一些变化。 You can now access variables that are effectively final .您现在可以访问实际上是final 的变量。 Relevant snippet and example from the Oracle documentation (emphasis mine): Oracle 文档中的相关片段和示例(重点是我的):

However, starting in Java SE 8, a local class can access local variables and parameters of the enclosing block that are final or effectively final .但是,从 Java SE 8 开始,本地类可以访问封闭块的 final 或有效 final 的局部变量和参数。

Effectively final: A non-final variable or parameter whose value is never changed after it is initialized is effectively final .有效最终:非最终变量或参数在初始化后其值永远不会改变,实际上是最终的

For example, suppose that the variable numberLength is not declared final, and you add the highlighted assignment statement in the PhoneNumber constructor:例如,假设变量numberLength未声明为 final,并且您在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; }

Because of this assignment statement, the variable numberLength is not effectively final anymore.由于这个赋值语句,变量numberLength不再有效。 As a result, the Java compiler generates an error message similar to "local variables referenced from an inner class must be final or effectively final" where the inner class PhoneNumber tries to access the numberLength variable:因此,Java 编译器会生成类似于“从内部类引用的局部变量必须是最终的或有效的最终”的错误消息,其中内部类PhoneNumber尝试访问numberLength变量:

 if (currentNumber.length() == numberLength)

Starting in Java SE 8, if you declare the local class in a method, it can access the method's parameters.从 Java SE 8 开始,如果您在方法中声明本地类,则它可以访问该方法的参数。 For example, you can define the following method in the PhoneNumber local class:例如,您可以在 PhoneNumber 本地类中定义以下方法:

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

The method printOriginalNumbers accesses the parameters phoneNumber1 and phoneNumber2 of the method validatePhoneNumber该方法printOriginalNumbers访问参数phoneNumber1phoneNumber2方法的validatePhoneNumber

使它成为final有什么问题

public void doStuff (final String userID)

declare the method声明方法

public void doStuff(final String userID)

The value needs to be final so that the compiler can be sure it doesn't change.该值需要是最终的,以便编译器可以确保它不会改变。 This means the compiler can bind the value to the inner class at any time, without worrying about updates.这意味着编译器可以随时将值绑定到内部类,而无需担心更新。

The value isn't changing in your code so this is a safe change.该值不会在您的代码中更改,因此这是一个安全的更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM