无法引用在不同方法中定义的内部类内的非最终变量

发布于 2024-08-01 20:34:28 字数 779 浏览 8 评论 0原文

编辑: 当多个变量通过计时器运行多次时,我需要更改它们的值。 我需要在计时器的每次迭代中不断更新值。 我无法将值设置为最终值,因为这会阻止我更新值,但是我收到了我在下面的初始问题中描述的错误:

我之前写过以下内容:

我收到错误“无法引用在不同方法中定义的内部类中的非最终变量”。

这种情况发生在双重调用的价格和称为 PriceObject 的价格上。 你知道我为什么会遇到这个问题吗? 我不明白为什么我需要最终声明。 另外,如果您能看到我正在尝试做什么,我需要做什么来解决这个问题。

public static void main(String args[]) {

    int period = 2000;
    int delay = 2000;

    double lastPrice = 0;
    Price priceObject = new Price();
    double price = 0;

    Timer timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            price = priceObject.getNextPrice(lastPrice);
            System.out.println();
            lastPrice = price;
        }
    }, delay, period);
}

Edited:
I need to change the values of several variables as they run several times thorugh a timer. I need to keep updating the values with every iteration through the timer. I cannot set the values to final as that will prevent me from updating the values however I am getting the error I describe in the initial question below:

I had previously written what is below:

I am getting the error "cannot refer to a non-final variable inside an inner class defined in a different method".

This is happening for the double called price and the Price called priceObject. Do you know why I get this problem. I do not understand why I need to have a final declaration. Also if you can see what it is I am trying to do, what do I have to do to get around this problem.

public static void main(String args[]) {

    int period = 2000;
    int delay = 2000;

    double lastPrice = 0;
    Price priceObject = new Price();
    double price = 0;

    Timer timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            price = priceObject.getNextPrice(lastPrice);
            System.out.println();
            lastPrice = price;
        }
    }, delay, period);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(20

单身狗的梦 2024-08-08 20:34:28

Java不支持真正的闭包,即使使用像您一样的匿名类这里 (new TimerTask() { ... }) 看起来像是一种闭包。

编辑 - 请参阅下面的评论 - 正如 KeeperOfTheSoul 指出的那样,以下不是正确的解释。

这就是它不起作用的原因:

变量 lastPrice< /code> 和price 是main() 方法中的局部变量。 使用匿名类创建的对象可能会持续到 main() 方法返回之后。

main() 方法返回时,局部变量(例如 lastPriceprice)将从堆栈中清除,因此它们不会' main() 返回后不再存在。

但匿名类对象引用这些变量。 如果匿名类对象在变量被清理后尝试访问变量,事情就会变得非常错误。

通过将 lastPriceprice 设置为 final,它们不再是真正的变量,而是常量。 然后,编译器可以将匿名类中 lastPriceprice 的使用替换为常量的值(当然,在编译时),并且您不会不再有访问不存在的变量的问题。

其他支持闭包的编程语言通过特殊处理这些变量来实现这一点 - 确保它们在方法结束时不会被破坏,以便闭包仍然可以访问变量。

@Ankur:你可以这样做:

public static void main(String args[]) {
    int period = 2000;
    int delay = 2000;

    Timer timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {
        // Variables as member variables instead of local variables in main()
        private double lastPrice = 0;
        private Price priceObject = new Price();
        private double price = 0;

        public void run() {
            price = priceObject.getNextPrice(lastPrice);
            System.out.println();
            lastPrice = price;
        }
    }, delay, period);      
}

Java doesn't support true closures, even though using an anonymous class like you are using here (new TimerTask() { ... }) looks like a kind of closure.

edit - See the comments below - the following is not a correct explanation, as KeeperOfTheSoul points out.

This is why it doesn't work:

The variables lastPrice and price are local variables in the main() method. The object that you create with the anonymous class might last until after the main() method returns.

When the main() method returns, local variables (such as lastPrice and price) will be cleaned up from the stack, so they won't exist anymore after main() returns.

But the anonymous class object references these variables. Things would go horribly wrong if the anonymous class object tries to access the variables after they have been cleaned up.

By making lastPrice and price final, they are not really variables anymore, but constants. The compiler can then just replace the use of lastPrice and price in the anonymous class with the values of the constants (at compile time, of course), and you won't have the problem with accessing non-existent variables anymore.

Other programming languages that do support closures do it by treating those variables specially - by making sure they don't get destroyed when the method ends, so that the closure can still access the variables.

@Ankur: You could do this:

public static void main(String args[]) {
    int period = 2000;
    int delay = 2000;

    Timer timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {
        // Variables as member variables instead of local variables in main()
        private double lastPrice = 0;
        private Price priceObject = new Price();
        private double price = 0;

        public void run() {
            price = priceObject.getNextPrice(lastPrice);
            System.out.println();
            lastPrice = price;
        }
    }, delay, period);      
}
全部不再 2024-08-08 20:34:28

已经提供了关于为什么你不能做你想做的事情的很好的解释。 作为解决方案,也许可以考虑:

public class foo
{
    static class priceInfo
    {
        public double lastPrice = 0;
        public double price = 0;
        public Price priceObject = new Price ();
    }

    public static void main ( String args[] )
    {

        int period = 2000;
        int delay = 2000;

        final priceInfo pi = new priceInfo ();
        Timer timer = new Timer ();

        timer.scheduleAtFixedRate ( new TimerTask ()
        {
            public void run ()
            {
                pi.price = pi.priceObject.getNextPrice ( pi.lastPrice );
                System.out.println ();
                pi.lastPrice = pi.price;

            }
        }, delay, period );
    }
}

似乎您可以做比这更好的设计,但想法是您可以将更新的变量分组到不更改的类引用中。

Good explanations for why you can't do what you're trying to do already provided. As a solution, maybe consider:

public class foo
{
    static class priceInfo
    {
        public double lastPrice = 0;
        public double price = 0;
        public Price priceObject = new Price ();
    }

    public static void main ( String args[] )
    {

        int period = 2000;
        int delay = 2000;

        final priceInfo pi = new priceInfo ();
        Timer timer = new Timer ();

        timer.scheduleAtFixedRate ( new TimerTask ()
        {
            public void run ()
            {
                pi.price = pi.priceObject.getNextPrice ( pi.lastPrice );
                System.out.println ();
                pi.lastPrice = pi.price;

            }
        }, delay, period );
    }
}

Seems like probably you could do a better design than that, but the idea is that you could group the updated variables inside a class reference that doesn't change.

一口甜 2024-08-08 20:34:28

使用匿名类时,只能从包含类访问最终变量。 因此,您需要将使用的变量声明为final(这不是您的选择,因为您要更改lastPriceprice),或者不要使用匿名类。

因此,您的选择是创建一个实际的内部类,您可以在其中传递变量并以正常方式使用它们,

或者:

有一个针对您的 lastPrice 的快速(在我看来丑陋的)技巧和 price 变量,它是这样声明的

final double lastPrice[1];
final double price[1];

,在您的匿名类中,您可以像这样设置值

price[0] = priceObject.getNextPrice(lastPrice[0]);
System.out.println();
lastPrice[0] = price[0];

You can only access final variables from the containing class when using an anonymous class. Therefore you need to declare the variables being used final (which is not an option for you since you are changing lastPrice and price), or don't use an anonymous class.

So your options are to create an actual inner class, in which you can pass in the variables and use them in a normal fashion

or:

There is a quick (and in my opinion ugly) hack for your lastPrice and price variable which is to declare it like so

final double lastPrice[1];
final double price[1];

and in your anonymous class you can set the value like this

price[0] = priceObject.getNextPrice(lastPrice[0]);
System.out.println();
lastPrice[0] = price[0];
み零 2024-08-08 20:34:28

为了避免匿名委托引用的 java 变量中的闭包产生奇怪的副作用,必须将其标记为 Final,因此要在计时器任务中引用 lastPrice 和 Price,需要将它们标记为 Final。

这显然对您不起作用,因为您希望更改它们,在这种情况下,您应该考虑将它们封装在一个类中。

public class Foo {
    private PriceObject priceObject;
    private double lastPrice;
    private double price;

    public Foo(PriceObject priceObject) {
        this.priceObject = priceObject;
    }

    public void tick() {
        price = priceObject.getNextPrice(lastPrice);
        lastPrice = price;
    }
}

现在只需创建一个新的 Foo 作为最终对象并从计时器中调用 .tick 即可。

public static void main(String args[]){
    int period = 2000;
    int delay = 2000;

    Price priceObject = new Price();
    final Foo foo = new Foo(priceObject);

    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            foo.tick();
        }
    }, delay, period);
}

To avoid strange side-effects with closures in java variables referenced by an anonymous delegate must be marked as final, so to refer to lastPrice and price within the timer task they need to be marked as final.

This obviously won't work for you because you wish to change them, in this case you should look at encapsulating them within a class.

public class Foo {
    private PriceObject priceObject;
    private double lastPrice;
    private double price;

    public Foo(PriceObject priceObject) {
        this.priceObject = priceObject;
    }

    public void tick() {
        price = priceObject.getNextPrice(lastPrice);
        lastPrice = price;
    }
}

now just create a new Foo as final and call .tick from the timer.

public static void main(String args[]){
    int period = 2000;
    int delay = 2000;

    Price priceObject = new Price();
    final Foo foo = new Foo(priceObject);

    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            foo.tick();
        }
    }, delay, period);
}
泅人 2024-08-08 20:34:28

对于匿名类,您实际上是在声明一个“无名”嵌套类。 对于嵌套类,编译器生成一个新的独立公共类,其构造函数将使用它用作参数的所有变量(对于“命名”嵌套类,这始终是原始/封闭类的实例)。 这样做是因为运行时环境没有嵌套类的概念,因此需要从嵌套类到独立类的(自动)转换。

以这段代码为例:

public class EnclosingClass {
    public void someMethod() {
        String shared = "hello"; 
        new Thread() {
            public void run() {
                // this is not valid, won't compile
                System.out.println(shared); // this instance expects shared to point to the reference where the String object "hello" lives in heap
            }
        }.start();

        // change the reference 'shared' points to, with a new value
        shared = "other hello"; 
        System.out.println(shared);
    }
}

这是行不通的,因为这是编译器在幕后所做的事情:

public void someMethod() {
    String shared = "hello"; 
    new EnclosingClass$1(shared).start();

    // change the reference 'shared' points to, with a new value
    shared = "other hello"; 
    System.out.println(shared);
}

原始的匿名类被编译器生成的一些独立类替换(代码不准确,但应该给你一个好主意):

public class EnclosingClass$1 extends Thread {
    String shared;
    public EnclosingClass$1(String shared) {
        this.shared = shared;
    }

    public void run() {
        System.out.println(shared);
    }
}

正如你所看到的,独立类持有对共享对象的引用,请记住java中的所有内容都是按值传递的,因此即使EnendingClass中的引用变量“shared”发生更改,它指向的实例也是未修改,并且指向它的所有其他引用变量(如匿名类中的引用变量:Enleading$1)将不会意识到这一点。 这是编译器强制您将这种“共享”变量声明为最终变量的主要原因,这样这种类型的行为就不会进入您已经运行的代码中。

现在,这就是当您在匿名类中使用实例变量时会发生的情况(这是您应该解决问题的方法,将您的逻辑移至“实例”方法或类的构造函数):

public class EnclosingClass {
    String shared = "hello";
    public void someMethod() {
        new Thread() {
            public void run() {
                System.out.println(shared); // this is perfectly valid
            }
        }.start();

        // change the reference 'shared' points to, with a new value
        shared = "other hello"; 
        System.out.println(shared);
    }
}

这编译得很好,因为编译器将修改代码,以便新生成的类 Enleading$1 将保留对其实例化的 EnendingClass 实例的引用(这只是一种表示,但应该可以让您继续):

public void someMethod() {
    new EnclosingClass$1(this).start();

    // change the reference 'shared' points to, with a new value
    shared = "other hello"; 
    System.out.println(shared);
}

public class EnclosingClass$1 extends Thread {
    EnclosingClass enclosing;
    public EnclosingClass$1(EnclosingClass enclosing) {
        this.enclosing = enclosing;
    }

    public void run() {
        System.out.println(enclosing.shared);
    }
}

像这样,当引用变量 ' EnclosureClass 中的“shared”被重新分配,这发生在调用 Thread#run() 之前,您将看到“other hello”打印两次,因为现在 EnendingClass$1#enending 变量将保留对其所在类的对象的引用已声明,因此对该对象的任何属性的更改对于 EnendingClass$1 的实例都是可见的。

有关该主题的更多信息,您可以查看这篇优秀的博客文章(不是我写的):http://kevinboone.net/java_inner.html

With anonymous classes, you are actually declaring a "nameless" nested class. For nested classes, the compiler generates a new standalone public class with a constructor that will take all the variables it uses as arguments (for "named" nested classes, this is always an instance of the original/enclosing class). This is done because the runtime environment has no notion of nested classes, so there needs to be a (automatic) conversion from a nested to a standalone class.

Take this code for example:

public class EnclosingClass {
    public void someMethod() {
        String shared = "hello"; 
        new Thread() {
            public void run() {
                // this is not valid, won't compile
                System.out.println(shared); // this instance expects shared to point to the reference where the String object "hello" lives in heap
            }
        }.start();

        // change the reference 'shared' points to, with a new value
        shared = "other hello"; 
        System.out.println(shared);
    }
}

That won't work, because this is what the compiler does under the hood:

public void someMethod() {
    String shared = "hello"; 
    new EnclosingClass$1(shared).start();

    // change the reference 'shared' points to, with a new value
    shared = "other hello"; 
    System.out.println(shared);
}

The original anonymous class is replaced by some standalone class that the compiler generates (code is not exact, but should give you a good idea):

public class EnclosingClass$1 extends Thread {
    String shared;
    public EnclosingClass$1(String shared) {
        this.shared = shared;
    }

    public void run() {
        System.out.println(shared);
    }
}

As you can see, the standalone class holds a reference to the shared object, remember that everything in java is pass-by-value, so even if the reference variable 'shared' in EnclosingClass gets changed, the instance it points to is not modified, and all other reference variables pointing to it (like the one in the anonymous class: Enclosing$1), will not be aware of this. This is the main reason the compiler forces you to declare this 'shared' variables as final, so that this type of behavior won't make it into your already running code.

Now, this is what happens when you use an instance variable inside an anonymous class (this is what you should do to solve your problem, move your logic to an "instance" method or a constructor of a class):

public class EnclosingClass {
    String shared = "hello";
    public void someMethod() {
        new Thread() {
            public void run() {
                System.out.println(shared); // this is perfectly valid
            }
        }.start();

        // change the reference 'shared' points to, with a new value
        shared = "other hello"; 
        System.out.println(shared);
    }
}

This compiles fine, because the compiler will modify the code, so that the new generated class Enclosing$1 will hold a reference to the instance of EnclosingClass where it was instantiated (this is only a representation, but should get you going):

public void someMethod() {
    new EnclosingClass$1(this).start();

    // change the reference 'shared' points to, with a new value
    shared = "other hello"; 
    System.out.println(shared);
}

public class EnclosingClass$1 extends Thread {
    EnclosingClass enclosing;
    public EnclosingClass$1(EnclosingClass enclosing) {
        this.enclosing = enclosing;
    }

    public void run() {
        System.out.println(enclosing.shared);
    }
}

Like this, when the reference variable 'shared' in EnclosingClass gets reassigned, and this happens before the call to Thread#run(), you'll see "other hello" printed twice, because now EnclosingClass$1#enclosing variable will keep a reference to the object of the class where it was declared, so changes to any attribute on that object will be visible to instances of EnclosingClass$1.

For more information on the subject, you can see this excelent blog post (not written by me): http://kevinboone.net/java_inner.html

套路撩心 2024-08-08 20:34:28

当我偶然发现这个问题时,我只是通过构造函数将对象传递给内部类。 如果我需要传递基元或不可变对象(如本例所示),则需要一个包装类。

编辑:实际上,我根本不使用匿名类,而是使用适当的子类:

public class PriceData {
        private double lastPrice = 0;
        private double price = 0;

        public void setlastPrice(double lastPrice) {
            this.lastPrice = lastPrice;
        }

        public double getLastPrice() {
            return lastPrice;
        }

        public void setPrice(double price) {
            this.price = price;
        }

        public double getPrice() {
            return price;
        }
    }

    public class PriceTimerTask extends TimerTask {
        private PriceData priceData;
        private Price priceObject;

        public PriceTimerTask(PriceData priceData, Price priceObject) {
            this.priceData = priceData;
            this.priceObject = priceObject;
        }

        public void run() {
            priceData.setPrice(priceObject.getNextPrice(lastPrice));
            System.out.println();
            priceData.setLastPrice(priceData.getPrice());

        }
    }

    public static void main(String args[]) {

        int period = 2000;
        int delay = 2000;

        PriceData priceData = new PriceData();
        Price priceObject = new Price();

        Timer timer = new Timer();

        timer.scheduleAtFixedRate(new PriceTimerTask(priceData, priceObject), delay, period);
    }

When I stumble upon this issue, I just pass the objects to the inner class through the constructor. If I need to pass primitives or immutable objects (as in this case), a wrapper class is needed.

Edit: Actually, I don't use an anonymous class at all, but a proper subclass:

public class PriceData {
        private double lastPrice = 0;
        private double price = 0;

        public void setlastPrice(double lastPrice) {
            this.lastPrice = lastPrice;
        }

        public double getLastPrice() {
            return lastPrice;
        }

        public void setPrice(double price) {
            this.price = price;
        }

        public double getPrice() {
            return price;
        }
    }

    public class PriceTimerTask extends TimerTask {
        private PriceData priceData;
        private Price priceObject;

        public PriceTimerTask(PriceData priceData, Price priceObject) {
            this.priceData = priceData;
            this.priceObject = priceObject;
        }

        public void run() {
            priceData.setPrice(priceObject.getNextPrice(lastPrice));
            System.out.println();
            priceData.setLastPrice(priceData.getPrice());

        }
    }

    public static void main(String args[]) {

        int period = 2000;
        int delay = 2000;

        PriceData priceData = new PriceData();
        Price priceObject = new Price();

        Timer timer = new Timer();

        timer.scheduleAtFixedRate(new PriceTimerTask(priceData, priceObject), delay, period);
    }
愿得七秒忆 2024-08-08 20:34:28

您不能引用非最终变量,因为 Java 语言规范是这样规定的。 从 8.1.3 开始:
“任何使用但未在内部类中声明的局部变量、形式方法参数或异常处理程序参数都必须声明为final。” 整个段落。
我只能看到你的代码的一部分 - 根据我的说法,调度修改局部变量是一个奇怪的想法。 当您离开该函数时,局部变量将不再存在。 也许类的静态字段会更好?

You cannot refer to non-final variables because Java Language Specification says so. From 8.1.3:
"Any local variable, formal method parameter or exception handler parameter used but not declared in an inner class must be declared final." Whole paragraph.
I can see only part of your code - according to me scheduling modification of local variables is a strange idea. Local variables cease to exist when you leave the function. Maybe static fields of a class would be better?

柠檬 2024-08-08 20:34:28

我只是写了一些东西来按照作者的意图处理一些事情。
我发现最好的办法是让构造函数获取所有对象,然后在实现的方法中使用该构造函数对象。

但是,如果您正在编写通用接口类,那么您必须传递一个对象,或者更好的是对象列表。 这可以通过 Object[] 甚至更好的 Object ... 来完成,因为它更容易调用。

请参阅下面我的示例片段。

List<String> lst = new ArrayList<String>();
lst.add("1");
lst.add("2");        

SomeAbstractClass p = new SomeAbstractClass (lst, "another parameter", 20, true) {            

    public void perform( ) {                           
        ArrayList<String> lst = (ArrayList<String>)getArgs()[0];                        
    }

};

public abstract class SomeAbstractClass{    
    private Object[] args;

    public SomeAbstractClass(Object ... args) {
        this.args = args;           
    }      

    public abstract void perform();        

    public Object[] getArgs() {
        return args;
    }

}

请参阅这篇关于 Java 闭包的文章,它支持开箱即用:
http://mseifed.blogspot.se /2012/09/closure-implementation-for-java-5-6-and.html

版本 1 支持通过自动转换传递非最终闭包:
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/Closure.java

    SortedSet<String> sortedNames = new TreeSet<String>();
    // NOTE! Instead of enforcing final, we pass it through the constructor
    eachLine(randomFile0, new V1<String>(sortedNames) {
        public void call(String line) {
            SortedSet<String> sortedNames = castFirst();  // Read contructor arg zero, and auto cast it
            sortedNames.add(extractName(line));
        }
    });

I just wrote something to handle something along the authors intention.
I found the best thing to do was to let the constructor take all the objects and then in your implemented method use that constructor objects.

However, if you are writing a generic interface class, then you have to pass an Object, or better a list of Objects. This could be done by Object[] or even better, Object ... because it is easier to call.

See my example piece just below.

List<String> lst = new ArrayList<String>();
lst.add("1");
lst.add("2");        

SomeAbstractClass p = new SomeAbstractClass (lst, "another parameter", 20, true) {            

    public void perform( ) {                           
        ArrayList<String> lst = (ArrayList<String>)getArgs()[0];                        
    }

};

public abstract class SomeAbstractClass{    
    private Object[] args;

    public SomeAbstractClass(Object ... args) {
        this.args = args;           
    }      

    public abstract void perform();        

    public Object[] getArgs() {
        return args;
    }

}

Please see this post about Java closures that supports this out of the box:
http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html

Version 1 supports passing of non-final closures with autocasting:
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/Closure.java

    SortedSet<String> sortedNames = new TreeSet<String>();
    // NOTE! Instead of enforcing final, we pass it through the constructor
    eachLine(randomFile0, new V1<String>(sortedNames) {
        public void call(String line) {
            SortedSet<String> sortedNames = castFirst();  // Read contructor arg zero, and auto cast it
            sortedNames.add(extractName(line));
        }
    });
信仰 2024-08-08 20:34:28

如果您想更改匿名类中方法调用中的值,则该“值”实际上是一个 Future。 所以,如果你使用 Guava,你可以写

...
final SettableFuture<Integer> myvalue = SettableFuture<Integer>.create();
...
someclass.run(new Runnable(){

    public void run(){
        ...
        myvalue.set(value);
        ...
    }
 }

 return myvalue.get();

If you want to change a value in a method call within an anonymous class, that "value" is actually a Future. So, if you use Guava, you can write

...
final SettableFuture<Integer> myvalue = SettableFuture<Integer>.create();
...
someclass.run(new Runnable(){

    public void run(){
        ...
        myvalue.set(value);
        ...
    }
 }

 return myvalue.get();
旧时光的容颜 2024-08-08 20:34:28

我注意到没有提到的一个解决方案(除非我错过了,如果我错过了请纠正我),那就是使用类变量。 尝试在方法中运行新线程时遇到此问题:new Thread(){ Do Something }

从下面调用 doSomething() 将会起作用。 您不必将其声明为final,只需更改变量的范围,以便它不会在内部类之前被收集。 当然,除非您的流程很大并且更改范围可能会产生某种冲突。 我不想让我的变量成为最终变量,因为它绝不是最终/常量。

public class Test
{

    protected String var1;
    protected String var2;

    public void doSomething()
    {
        new Thread()
        {
            public void run()
            {
                System.out.println("In Thread variable 1: " + var1);
                System.out.println("In Thread variable 2: " + var2);
            }
        }.start();
    }

}

One solution I have noticed isn't mentioned (unless I missed it, if I did please correct me), is the use of a class variable. Ran into this issue attempting to run a new thread within a method: new Thread(){ Do Something }.

Calling doSomething() from the following will work. You do not necessarily have to declare it final, just need to change the scope of the variable so it is not collected before the innerclass. This is unless of course your process is huge and changing the scope might create some sort of conflict. I didn't want to make my variable final as it was in no way a final/constant.

public class Test
{

    protected String var1;
    protected String var2;

    public void doSomething()
    {
        new Thread()
        {
            public void run()
            {
                System.out.println("In Thread variable 1: " + var1);
                System.out.println("In Thread variable 2: " + var2);
            }
        }.start();
    }

}
蘸点软妹酱 2024-08-08 20:34:28

主要关心的是匿名类实例内的变量是否可以在运行时解析。 只要保证变量在运行时作用域内,则不必将变量设为final。 例如,请查看 updateStatus() 方法中的两个变量 _statusMessage 和 _statusTextView。

public class WorkerService extends Service {

Worker _worker;
ExecutorService _executorService;
ScheduledExecutorService _scheduledStopService;

TextView _statusTextView;


@Override
public void onCreate() {
    _worker = new Worker(this);
    _worker.monitorGpsInBackground();

    // To get a thread pool service containing merely one thread
    _executorService = Executors.newSingleThreadExecutor();

    // schedule something to run in the future
    _scheduledStopService = Executors.newSingleThreadScheduledExecutor();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    ServiceRunnable runnable = new ServiceRunnable(this, startId);
    _executorService.execute(runnable);

    // the return value tells what the OS should
    // do if this service is killed for resource reasons
    // 1. START_STICKY: the OS restarts the service when resources become
    // available by passing a null intent to onStartCommand
    // 2. START_REDELIVER_INTENT: the OS restarts the service when resources
    // become available by passing the last intent that was passed to the
    // service before it was killed to onStartCommand
    // 3. START_NOT_STICKY: just wait for next call to startService, no
    // auto-restart
    return Service.START_NOT_STICKY;
}

@Override
public void onDestroy() {
    _worker.stopGpsMonitoring();
}

@Override
public IBinder onBind(Intent intent) {
    return null;
}

class ServiceRunnable implements Runnable {

    WorkerService _theService;
    int _startId;
    String _statusMessage;

    public ServiceRunnable(WorkerService theService, int startId) {
        _theService = theService;
        _startId = startId;
    }

    @Override
    public void run() {

        _statusTextView = MyActivity.getActivityStatusView();

        // get most recently available location as a latitude /
        // longtitude
        Location location = _worker.getLocation();
        updateStatus("Starting");

        // convert lat/lng to a human-readable address
        String address = _worker.reverseGeocode(location);
        updateStatus("Reverse geocoding");

        // Write the location and address out to a file
        _worker.save(location, address, "ResponsiveUx.out");
        updateStatus("Done");

        DelayedStopRequest stopRequest = new DelayedStopRequest(_theService, _startId);

        // schedule a stopRequest after 10 seconds
        _theService._scheduledStopService.schedule(stopRequest, 10, TimeUnit.SECONDS);
    }

    void updateStatus(String message) {
        _statusMessage = message;

        if (_statusTextView != null) {
            _statusTextView.post(new Runnable() {

                @Override
                public void run() {
                    _statusTextView.setText(_statusMessage);

                }

            });
        }
    }

}

The main concern is whether a variable inside the anonymous class instance can be resolved at run-time. It is not a must to make a variable final as long as it is guaranteed that the variable is inside the run-time scope. For example, please see the two variables _statusMessage and _statusTextView inside updateStatus() method.

public class WorkerService extends Service {

Worker _worker;
ExecutorService _executorService;
ScheduledExecutorService _scheduledStopService;

TextView _statusTextView;


@Override
public void onCreate() {
    _worker = new Worker(this);
    _worker.monitorGpsInBackground();

    // To get a thread pool service containing merely one thread
    _executorService = Executors.newSingleThreadExecutor();

    // schedule something to run in the future
    _scheduledStopService = Executors.newSingleThreadScheduledExecutor();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    ServiceRunnable runnable = new ServiceRunnable(this, startId);
    _executorService.execute(runnable);

    // the return value tells what the OS should
    // do if this service is killed for resource reasons
    // 1. START_STICKY: the OS restarts the service when resources become
    // available by passing a null intent to onStartCommand
    // 2. START_REDELIVER_INTENT: the OS restarts the service when resources
    // become available by passing the last intent that was passed to the
    // service before it was killed to onStartCommand
    // 3. START_NOT_STICKY: just wait for next call to startService, no
    // auto-restart
    return Service.START_NOT_STICKY;
}

@Override
public void onDestroy() {
    _worker.stopGpsMonitoring();
}

@Override
public IBinder onBind(Intent intent) {
    return null;
}

class ServiceRunnable implements Runnable {

    WorkerService _theService;
    int _startId;
    String _statusMessage;

    public ServiceRunnable(WorkerService theService, int startId) {
        _theService = theService;
        _startId = startId;
    }

    @Override
    public void run() {

        _statusTextView = MyActivity.getActivityStatusView();

        // get most recently available location as a latitude /
        // longtitude
        Location location = _worker.getLocation();
        updateStatus("Starting");

        // convert lat/lng to a human-readable address
        String address = _worker.reverseGeocode(location);
        updateStatus("Reverse geocoding");

        // Write the location and address out to a file
        _worker.save(location, address, "ResponsiveUx.out");
        updateStatus("Done");

        DelayedStopRequest stopRequest = new DelayedStopRequest(_theService, _startId);

        // schedule a stopRequest after 10 seconds
        _theService._scheduledStopService.schedule(stopRequest, 10, TimeUnit.SECONDS);
    }

    void updateStatus(String message) {
        _statusMessage = message;

        if (_statusTextView != null) {
            _statusTextView.post(new Runnable() {

                @Override
                public void run() {
                    _statusTextView.setText(_statusMessage);

                }

            });
        }
    }

}
影子是时光的心 2024-08-08 20:34:28

对我有用的只是在你的函数之外定义变量。

就在主函数声明之前,即

Double price;
public static void main(String []args(){
--------
--------
}

what worked for me is just define the variable outside this function of your.

Just before main function declare i.e.

Double price;
public static void main(String []args(){
--------
--------
}
↘紸啶 2024-08-08 20:34:28

将变量声明为静态变量,并使用 className.variable 在所需方法中引用它

Declare the variable as a static and reference it in the required method using className.variable

红ご颜醉 2024-08-08 20:34:28

只是另一种解释。 考虑下面这个例子

public class Outer{
     public static void main(String[] args){
         Outer o = new Outer();
         o.m1();        
         o=null;
     }
     public void m1(){
         //int x = 10;
         class Inner{
             Thread t = new Thread(new Runnable(){
                 public void run(){
                     for(int i=0;i<10;i++){
                         try{
                             Thread.sleep(2000);                            
                         }catch(InterruptedException e){
                             //handle InterruptedException e
                         }
                         System.out.println("Thread t running");                             
                     }
                 }
             });
         }
         new Inner().t.start();
         System.out.println("m1 Completes");
    }
}

,这里的输出将是

m1 Completes

Thread t running

Thread t running

Thread t running

........

现在方法 m1() 完成,我们将引用变量 o 分配给 null ,现在外部类对象有资格进行GC,但内部类对象仍然存在,并且与正在运行的线程对象具有(Has-A)关系。 如果没有现有的外部类对象,就不可能存在 m1() 方法,如果没有现有的 m1() 方法,就不可能存在其局部变量,但如果内部类对象使用 m1() 方法的局部变量,那么一切都是不言自明的。

为了解决这个问题,我们必须创建局部变量的副本,然后必须将其复制到带有内部类对象的堆中,java仅对最终变量执行此操作,因为它们实际上不是变量,它们就像常量(一切都只发生在编译时)不在运行时)。

Just an another explanation. Consider this example below

public class Outer{
     public static void main(String[] args){
         Outer o = new Outer();
         o.m1();        
         o=null;
     }
     public void m1(){
         //int x = 10;
         class Inner{
             Thread t = new Thread(new Runnable(){
                 public void run(){
                     for(int i=0;i<10;i++){
                         try{
                             Thread.sleep(2000);                            
                         }catch(InterruptedException e){
                             //handle InterruptedException e
                         }
                         System.out.println("Thread t running");                             
                     }
                 }
             });
         }
         new Inner().t.start();
         System.out.println("m1 Completes");
    }
}

Here Output will be

m1 Completes

Thread t running

Thread t running

Thread t running

................

Now method m1() completes and we assign reference variable o to null , Now Outer Class Object is eligible for GC but Inner Class Object is still exist who has (Has-A) relationship with Thread object which is running. Without existing Outer class object there is no chance of existing m1() method and without existing m1() method there is no chance of existing its local variable but if Inner Class Object uses the local variable of m1() method then everything is self explanatory.

To solve this we have to create a copy of local variable and then have to copy then into the heap with Inner class object, what java does for only final variable because they are not actually variable they are like constants(Everything happens at compile time only not at runtime).

杯别 2024-08-08 20:34:28

为了解决上述问题,不同的语言做出了不同的决定。

对于 Java,解决方案正如我们在本文中看到的那样。

对于 C#,解决方案是允许副作用,并且通过引用捕获是唯一的选择。

对于C++11,解决方案是让程序员做出决定。 他们可以选择按值捕获或按引用捕获。 如果按值捕获,则不会发生副作用,因为引用的变量实际上是不同的。 如果通过引用捕获,可能会出现副作用,但程序员应该意识到这一点。

To solve the problem above, different languages make different decisions.

for Java, the solution is as what we see in this article.

for C#, the solution is allow side-effects and capture by reference is the only option.

for C++11, the solution is to allow the programmer make the decision. They can choose to capture by value or by reference. If capturing by value, no side-effects would occur because the variable referenced is actually different. If capture by reference, side-effects may occur but the programmer should realize it.

一梦等七年七年为一梦 2024-08-08 20:34:28

因为如果变量不是最终变量,就会令人困惑,因为对它的更改不会在匿名类中被拾取。

只需将变量“price”和“lastPrice”设为最终变量即可。

-- 编辑

Oops,显然,您还需要在函数中不要分配给它们。 您将需要新的局部变量。 不管怎样,我怀疑现在已经有人给了你更好的答案。

Because it's confusing if the variable isn't final, as the changes to it won't be picked up in the anonymous class.

Just make the variables 'price' and 'lastPrice' final.

-- Edit

Oops, and you'll also need to not assign to them, obviously, in your function. You'll need new local variables. Anyway, I suspect someone has given you a better answer by now.

小瓶盖 2024-08-08 20:34:28

您可以在外部类之外声明变量。 之后,您将能够在内部类中编辑变量。 我有时在 android 中编码时会遇到类似的问题,所以我将变量声明为全局变量,它对我有用。

you can just declare the variable outside the outer class. After this, you will be able to edit the variable from within the inner class. I sometimes face similar problems while coding in android so I declare the variable as global and it works for me.

苏辞 2024-08-08 20:34:28

如果变量需要是最终的,但不能是,那么您可以将该变量的值分配给另一个变量并将其设为最终变量,以便您可以使用它。

If the variable required to be final, cannot be then you can assign the value of the variable to another variable and make THAT final so you can use it instead.

陈甜 2024-08-08 20:34:28

使用 ClassName.this.variableName 引用非最终变量

use ClassName.this.variableName to reference the non-final variable

仅一夜美梦 2024-08-08 20:34:28

您可以制作匿名内部类的 lastPricepriceObjectprice 字段吗?

Can you make lastPrice, priceObject, and price fields of the anonymous inner class?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文