Không thể tham chiếu đến một biến không cuối cùng bên trong một lớp bên trong được định nghĩa trong một phương thức khác


247

Đã chỉnh sửa: Tôi cần thay đổi giá trị của một số biến khi chúng chạy nhiều lần trong bộ đếm thời gian. Tôi cần tiếp tục cập nhật các giá trị với mỗi lần lặp thông qua bộ đếm thời gian. Tôi không thể đặt các giá trị thành cuối cùng vì điều đó sẽ ngăn tôi cập nhật các giá trị tuy nhiên tôi nhận được lỗi tôi mô tả trong câu hỏi ban đầu bên dưới:

Trước đây tôi đã viết những gì dưới đây:

Tôi nhận được lỗi "không thể tham chiếu đến một biến không phải là cuối cùng bên trong một lớp bên trong được định nghĩa theo một phương thức khác".

Điều này đang xảy ra đối với giá được gọi là gấp đôi và Giá được gọi là priceObject. Bạn có biết tại sao tôi gặp vấn đề này. Tôi không hiểu tại sao tôi cần phải có một tuyên bố cuối cùng. Ngoài ra nếu bạn có thể thấy những gì tôi đang cố gắng làm, tôi phải làm gì để khắc phục vấn đề này.

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

Những gì tôi đang hỏi là, làm thế nào để tôi có được một biến trong một bộ đếm thời gian mà tôi có thể liên tục cập nhật.
Ankur

1
@Ankur: câu trả lời đơn giản là "Không". Nhưng bạn có thể đạt được hiệu quả mong muốn bằng cách sử dụng một lớp bên trong; xem câu trả lời của @ petercardona.
Stephen C

Câu trả lời:


197

Java không hỗ trợ các bao đóng thực sự , mặc dù sử dụng một lớp ẩn danh như bạn đang sử dụng ở đây ( new TimerTask() { ... }) trông giống như một kiểu đóng.

chỉnh sửa - Xem các bình luận bên dưới - sau đây không phải là một lời giải thích chính xác, như KeeperOfTheSoul chỉ ra.

Đây là lý do tại sao nó không hoạt động:

Các biến lastPricevà giá là các biến cục bộ trong phương thức main (). Đối tượng mà bạn tạo với lớp ẩn danh có thể tồn tại cho đến khi main()phương thức trả về.

Khi main()phương thức trả về, các biến cục bộ (như lastPriceprice) sẽ được dọn sạch khỏi ngăn xếp, vì vậy chúng sẽ không tồn tại nữa sau khi main()trả về.

Nhưng đối tượng lớp ẩn danh tham chiếu các biến này. Mọi thứ sẽ trở nên sai lầm khủng khiếp nếu đối tượng lớp ẩn danh cố gắng truy cập các biến sau khi chúng được dọn sạch.

Bằng cách thực hiện lastPriceprice final, chúng không thực sự là biến nữa, mà là hằng số. Trình biên dịch sau đó có thể chỉ cần thay thế việc sử dụng lastPricepricetrong lớp ẩn danh bằng các giá trị của hằng số (tất nhiên là tại thời gian biên dịch), và bạn sẽ không gặp vấn đề với việc truy cập các biến không tồn tại nữa.

Các ngôn ngữ lập trình khác hỗ trợ các bao đóng thực hiện điều đó bằng cách xử lý các biến đó một cách đặc biệt - bằng cách đảm bảo chúng không bị phá hủy khi phương thức kết thúc, để đóng vẫn có thể truy cập các biến.

@Ankur: Bạn có thể làm điều này:

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

34
Không hoàn toàn đúng, Java tạo ra các hàm bắt cho các biến được đề cập để nắm bắt các giá trị thời gian chạy của chúng, chỉ là chúng muốn tránh một hiệu ứng phụ lạ có thể có trong .Net khi bạn nắm bắt giá trị trong ủy nhiệm, thay đổi giá trị trong phương thức bên ngoài và bây giờ đại biểu thấy giá trị mới thấy, stackoverflow.com/questions/271440/c-captured-variable-in-loop cho ví dụ C # về hành vi này mà Java nhắm tới.
Chris Chilvers

14
Đó không phải là "hiệu ứng phụ lạ", đó là hành vi bình thường mà mọi người mong đợi - và Java không thể cung cấp nó không tạo ra các ảnh chụp. Như một giải pháp thay thế, các biến cục bộ được sử dụng trong một lớp ẩn danh phải là cuối cùng.
Michael Borgwardt

12
Jesper, có lẽ bạn nên chỉnh sửa các phần không chính xác trong câu trả lời của mình thay vì chỉ cần có một thông báo nói ở trên là không chính xác.
James McMahon

19
Trên thực tế, Java không hỗ trợ các bao đóng. Các ngôn ngữ hỗ trợ các bao đóng làm như vậy bằng cách lưu trữ toàn bộ môi trường cục bộ (nghĩa là tập hợp các biến cục bộ được xác định trong khung ngăn xếp hiện tại) làm đối tượng heap. Java không hỗ trợ cho việc này (các nhà thiết kế ngôn ngữ muốn triển khai nó nhưng đã hết thời gian), vì vậy, một cách giải quyết, bất cứ khi nào một lớp cục bộ được khởi tạo, các giá trị của bất kỳ biến cục bộ nào mà nó đề cập đều được sao chép vào heap . Tuy nhiên, JVM sau đó không thể giữ các giá trị đồng bộ với các biến cục bộ, đó là lý do tại sao chúng phải là cuối cùng.
Taymon

64
Câu trả lời này hoàn toàn khó hiểu khi không có ai tên là "KeeperOfTheSoul" đã bình luận về nó. Câu trả lời nên được sửa đổi.
Adam Parkin

32

Để tránh các tác dụng phụ lạ với việc đóng trong các biến java được tham chiếu bởi một đại biểu ẩn danh phải được đánh dấu là cuối cùng, do đó, để tham khảo lastPricevà giá trong tác vụ hẹn giờ mà chúng cần được đánh dấu là cuối cùng.

Điều này rõ ràng sẽ không hiệu quả với bạn vì bạn muốn thay đổi chúng, trong trường hợp này bạn nên xem xét việc gói chúng trong một lớp.

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

bây giờ chỉ cần tạo một Foo mới là cuối cùng và gọi .tick từ bộ đếm thời gian.

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

1
hoặc bạn chỉ có thể dẫn Foo thực hiện Runnable ..?
vidstige

18

Bạn chỉ có thể truy cập các biến cuối cùng từ lớp chứa khi sử dụng lớp ẩn danh. Do đó, bạn cần khai báo các biến đang được sử dụng cuối cùng (đây không phải là một tùy chọn cho bạn vì bạn đang thay đổi Lastpriceprice ) hoặc không sử dụng một lớp ẩn danh.

Vì vậy, các tùy chọn của bạn là tạo một lớp bên trong thực tế, trong đó bạn có thể chuyển các biến và sử dụng chúng theo cách thông thường

hoặc là:

Có một cách nhanh chóng (và theo ý kiến của tôi xấu xí) hack cho bạn lastPricegiá biến đó là phải khai báo nó như vậy

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

và trong lớp ẩn danh của bạn, bạn có thể đặt giá trị như thế này

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

14

Giải thích tốt về lý do tại sao bạn không thể làm những gì bạn đang cố gắng cung cấp. Là một giải pháp, có thể xem xét:

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

Có vẻ như bạn có thể làm một thiết kế tốt hơn thế, nhưng ý tưởng là bạn có thể nhóm các biến được cập nhật bên trong một tham chiếu lớp không thay đổi.


11

Với các lớp ẩn danh, bạn thực sự đang khai báo một lớp lồng "không tên". Đối với các lớp lồng nhau, trình biên dịch tạo ra một lớp công khai độc lập mới với hàm tạo sẽ lấy tất cả các biến mà nó sử dụng làm đối số (đối với các lớp lồng nhau "có tên", đây luôn là một thể hiện của lớp gốc / lớp kèm theo). Điều này được thực hiện bởi vì môi trường thời gian chạy không có khái niệm về các lớp lồng nhau, do đó cần phải có một chuyển đổi (tự động) từ một lớp lồng vào một lớp độc lập.

Lấy mã này làm ví dụ:

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

Điều đó sẽ không hoạt động, bởi vì đây là những gì trình biên dịch thực hiện dưới mui xe:

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

Lớp ẩn danh ban đầu được thay thế bằng một số lớp độc lập mà trình biên dịch tạo ra (mã không chính xác, nhưng sẽ cho bạn một ý tưởng hay):

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

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

Như bạn có thể thấy, lớp độc lập giữ một tham chiếu đến đối tượng được chia sẻ, hãy nhớ rằng mọi thứ trong java là giá trị truyền qua, vì vậy ngay cả khi biến tham chiếu 'được chia sẻ' trong EnclosesClass bị thay đổi, thì thể hiện của nó không bị sửa đổi và tất cả các biến tham chiếu khác trỏ đến nó (như biến trong lớp ẩn danh: Đóng $ 1), sẽ không nhận thức được điều này. Đây là lý do chính mà trình biên dịch buộc bạn phải khai báo các biến 'được chia sẻ' này là cuối cùng, để loại hành vi này sẽ không biến nó thành mã đang chạy của bạn.

Bây giờ, đây là những gì xảy ra khi bạn sử dụng một biến đối tượng bên trong một lớp ẩn danh (đây là điều bạn nên làm để giải quyết vấn đề của mình, chuyển logic của bạn sang một phương thức "thể hiện" hoặc một hàm tạo của một lớp):

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

Điều này biên dịch tốt, bởi vì trình biên dịch sẽ sửa đổi mã, do đó, lớp được tạo mới Bao gồm $ 1 sẽ giữ một tham chiếu đến thể hiện của EnclosesClass nơi nó được khởi tạo (đây chỉ là một đại diện, nhưng sẽ giúp bạn đi):

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

Giống như thế này, khi biến tham chiếu 'được chia sẻ' trong EnclosesClass được gán lại và điều này xảy ra trước khi gọi đến Thread # run (), bạn sẽ thấy "lời chào khác" được in hai lần, vì bây giờ biến EnclosesClass $ 1 # sẽ giữ tham chiếu đối tượng của lớp nơi nó được khai báo, vì vậy những thay đổi đối với bất kỳ thuộc tính nào trên đối tượng đó sẽ hiển thị với các thể hiện của EnclosesClass $ 1.

Để biết thêm thông tin về chủ đề này, bạn có thể xem bài đăng blog xuất sắc này (không phải do tôi viết): http://kevinboone.net/java_inner.html


Điều gì xảy ra nếu biến cục bộ 'chia sẻ' là một đối tượng có thể thay đổi? Theo giải thích của bạn, tuyên bố 'cuối cùng' sẽ không giúp được gì, phải không?
sactiw

Khai báo "chia sẻ" là cuối cùng sẽ cho phép bạn sửa đổi trạng thái của đối tượng tham chiếu biến cuối cùng, nhưng đối với ví dụ cụ thể này sẽ không hoạt động vì bạn sẽ không thể thay đổi giá trị của biến "chia sẻ" (mà là những gì OP muốn), bạn sẽ có thể sử dụng nó trong các lớp ẩn danh, nhưng giá trị của nó sẽ không thay đổi (vì nó được tuyên bố là cuối cùng). Điều quan trọng là phải nhận thấy sự khác biệt giữa các biến và giá trị thực mà chúng nắm giữ (có thể là nguyên thủy hoặc tham chiếu đến các đối tượng trong heap).
emerino

>>> nhưng giá trị của nó sẽ không thay đổi Tôi đoán bạn đang thiếu điểm tức là nếu biến tham chiếu cuối cùng đang trỏ đến một đối tượng có thể thay đổi thì nó vẫn có thể được cập nhật, tuy nhiên, lớp ẩn danh tạo ra bản sao nông do đó các thay đổi được phản ánh trong ẩn danh lớp học. Nói cách khác, trạng thái là đồng bộ, đó là những gì mong muốn ở đây. Ở đây, OP cần có khả năng sửa đổi biến được chia sẻ (kiểu nguyên thủy) và để đạt được điều đó OP sẽ cần phải bọc giá trị dưới một đối tượng có thể thay đổi và chia sẻ đối tượng có thể thay đổi đó.
sactiw

1
Tất nhiên, OP có thể bao bọc giá trị cần thiết dưới một đối tượng có thể thay đổi, khai báo biến là cuối cùng và sử dụng thay thế. Tuy nhiên, anh ta có thể tránh sử dụng một đối tượng phụ bằng cách khai báo biến là một thuộc tính của lớp hiện tại (như được chỉ ra và giải thích trong câu trả lời). Buộc các đối tượng có thể thay đổi (như sử dụng mảng chỉ để có thể sửa đổi giá trị của biến được chia sẻ) không phải là một ý tưởng hay.
emerino

7

Khi tôi vấp phải vấn đề này, tôi chỉ chuyển các đối tượng đến lớp bên trong thông qua hàm tạo. Nếu tôi cần vượt qua các đối tượng nguyên thủy hoặc bất biến (như trong trường hợp này), thì một lớp bao bọc là cần thiết.

Chỉnh sửa: Trên thực tế, tôi hoàn toàn không sử dụng một lớp ẩn danh, mà là một lớp con thích hợp:

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

2

Bạn không thể tham khảo các biến không phải là cuối cùng vì Đặc tả ngôn ngữ Java nói như vậy. Từ 8.1.3:
"Bất kỳ biến cục bộ, tham số phương thức chính thức hoặc tham số xử lý ngoại lệ nào được sử dụng nhưng không được khai báo trong lớp bên trong phải được khai báo cuối cùng." Toàn bộ đoạn văn.
Tôi chỉ có thể thấy một phần mã của bạn - theo tôi lập lịch sửa đổi các biến cục bộ là một ý tưởng lạ. Các biến cục bộ không còn tồn tại khi bạn rời khỏi hàm. Có lẽ các trường tĩnh của một lớp sẽ tốt hơn?


2

Tôi chỉ viết một cái gì đó để xử lý một cái gì đó theo ý định của tác giả . Tôi thấy điều tốt nhất cần làm là để cho hàm tạo lấy tất cả các đối tượng và sau đó trong phương thức được triển khai của bạn sử dụng các đối tượng của hàm tạo đó.

Tuy nhiên, nếu bạn đang viết một lớp giao diện chung, thì bạn phải vượt qua một Đối tượng hoặc tốt hơn là một danh sách các Đối tượng. Điều này có thể được thực hiện bởi Object [] hoặc thậm chí tốt hơn, Object ... bởi vì nó dễ gọi hơn.

Xem ví dụ của tôi ngay bên dưới.

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

}

Vui lòng xem bài đăng này về các bao đóng Java hỗ trợ điều này ngay lập tức: http://mseifed.blogspot.se/2012/09/clenses-im THỰCation-for-java-6-6-and.html

Phiên bản 1 hỗ trợ vượt qua các lần đóng không phải là cuối cùng với tự động: https://github.com/MSeifeddo/Clenses-im THỰCation-for-Java-5-6-and-7 / blob / master / org / mo / clenses / v1
/ Clos.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));
        }
    });

2

Nếu bạn muốn thay đổi một giá trị trong một cuộc gọi phương thức trong một lớp ẩn danh, thì "giá trị" đó thực sự là một Future. Vì vậy, nếu bạn sử dụng ổi, bạn có thể viết

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

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

 return myvalue.get();

2

Một giải pháp tôi nhận thấy không được đề cập (trừ khi tôi bỏ lỡ, nếu tôi vui lòng sửa lại cho tôi), đó là sử dụng biến lớp. Gặp vấn đề này khi cố chạy một luồng mới trong một phương thức : new Thread(){ Do Something }.

Gọi doSomething()từ sau sẽ làm việc. Bạn không nhất thiết phải khai báo nó final, chỉ cần thay đổi phạm vi của biến để nó không được thu thập trước innerclass. Điều này trừ khi tất nhiên quá trình của bạn là rất lớn và việc thay đổi phạm vi có thể tạo ra một số loại xung đột. Tôi không muốn biến trận chung kết của mình thành trận chung kết vì nó không phải là trận chung kết / không đổi.

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

}

1

Nếu biến được yêu cầu là cuối cùng, không thể thì bạn có thể gán giá trị của biến cho biến khác và biến THAT thành cuối cùng để bạn có thể sử dụng nó thay thế.



1

bạn chỉ có thể khai báo biến ngoài lớp bên ngoài. Sau này, bạn sẽ có thể chỉnh sửa biến từ bên trong lớp bên trong. Đôi khi tôi gặp phải các vấn đề tương tự trong khi mã hóa trong Android vì vậy tôi khai báo biến là toàn cục và nó hoạt động với tôi.


Điều này không thực sự trả lời câu hỏi ... Đó là lý do tại sao bạn bị hạ cấp.
Stuart Siegler

0

Bạn có thể thực hiện lastPrice, priceObjectpricecác lĩnh vực của lớp bên trong ẩn danh?


0

Mối quan tâm chính là liệu một biến trong thể hiện của lớp ẩn danh có thể được giải quyết trong thời gian chạy hay không. Không nhất thiết phải tạo một biến cuối cùng miễn là đảm bảo rằng biến đó nằm trong phạm vi thời gian chạy. Ví dụ: vui lòng xem hai biến _statusMessage và _statusTextView bên trong phương thức updateStatus ().

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

                }

            });
        }
    }

}

0

những gì làm việc cho tôi chỉ là xác định các biến bên ngoài chức năng này của bạn.

Ngay trước khi hàm chính khai báo tức là

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

Điều đó sẽ không hoạt động, bạn đang khai báo một biến đối tượng, để sử dụng nó, bạn sẽ cần tạo một thể hiện bên trong phương thức chính của mình. Bạn nên cụ thể hơn hoặc chỉ cần thêm công cụ sửa đổi tĩnh vào biến 'giá'.
emerino

0

Khai báo biến dưới dạng tĩnh và tham chiếu nó trong phương thức được yêu cầu bằng className.variable


Non-static parameter cannot be referenced from a static context
stephen

@Shweta các biến cục bộ và tham số phương thức không thể được khai báo là 'tĩnh', hơn nữa, đó là về cách nó đã được thực hiện để cho phép các lớp trong các phương thức (các lớp ẩn danh cục bộ) tiếp tục truy cập vào các biến cục bộ và tham số phương thức ngay cả sau phương thức đã trả về tức là nó tạo các bản sao 'cuối cùng' của chúng và sử dụng chúng làm các biến thể hiện.
sactiw

0

Chỉ là một lời giải thích khác. Xem xét ví dụ này dưới đây

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

Ở đây đầu ra sẽ là

m1 hoàn thành

Chủ đề t đang chạy

Chủ đề t đang chạy

Chủ đề t đang chạy

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

Bây giờ phương thức m1 () hoàn thành và chúng ta gán biến tham chiếu o thành null, Bây giờ Đối tượng lớp ngoài đủ điều kiện cho GC nhưng Đối tượng lớp bên trong vẫn tồn tại, người có mối quan hệ (Has-A) với đối tượng Thread đang chạy. Không có đối tượng lớp ngoài hiện tại, không có cơ hội của phương thức m1 () hiện tại và không có phương thức m1 () hiện tại, không có cơ hội tồn tại biến cục bộ của nó nhưng nếu Đối tượng lớp bên trong sử dụng biến cục bộ của phương thức m1 () thì mọi thứ đều tự giải thích .

Để giải quyết điều này, chúng ta phải tạo một bản sao của biến cục bộ và sau đó phải sao chép vào heap với đối tượng lớp bên trong, java chỉ làm gì cho biến cuối cùng vì chúng không thực sự biến chúng giống như hằng số (Mọi thứ chỉ xảy ra ở thời gian biên dịch không ở thời gian chạy).


-1

Để giải quyết vấn đề trên, các ngôn ngữ khác nhau đưa ra các quyết định khác nhau.

đối với Java, giải pháp là như những gì chúng ta thấy trong bài viết này.

đối với C #, giải pháp cho phép tác dụng phụ và chụp theo tham chiếu là lựa chọn duy nhất.

đối với C ++ 11, giải pháp là cho phép lập trình viên đưa ra quyết định. Họ có thể chọn chụp theo giá trị hoặc bằng cách tham khảo. Nếu chụp theo giá trị, sẽ không có tác dụng phụ nào xảy ra do biến được tham chiếu thực sự khác nhau. Nếu chụp bằng tham chiếu, tác dụng phụ có thể xảy ra nhưng lập trình viên nên nhận ra điều đó.


-2

Bởi vì thật khó hiểu nếu biến không phải là cuối cùng, vì những thay đổi của nó sẽ không được chọn trong lớp ẩn danh.

Chỉ cần thực hiện các biến 'giá' và 'cuối cùng'.

-- Biên tập

Rất tiếc, và bạn cũng sẽ không cần phải gán cho chúng, rõ ràng, trong chức năng của bạn. Bạn sẽ cần các biến cục bộ mới. Dù sao, tôi nghi ngờ ai đó đã cho bạn một câu trả lời tốt hơn bây giờ.


2
nó không chỉ khó hiểu - nó hoàn toàn không chính xác, do đó trình biên dịch không cho phép nó.
Chii

Nhưng làm thế nào để tôi thay đổi các giá trị khi tôi cần?
Ankur

Không chỉ vì nó khó hiểu; điều này là do Java không hỗ trợ các bao đóng. Xem câu trả lời của tôi dưới đây. @Ankur: Bạn có thể tạo các biến thành viên biến của đối tượng lớp ẩn danh thay vì biến cục bộ trong hàm main ().
Jesper

Anh ấy đang sửa đổi chúng, vì vậy chúng không thể là cuối cùng.
Robin

Nếu giá và Lastprice là cuối cùng, các bài tập cho chúng sẽ không được biên dịch.
Greg Mattes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.