Làm thế nào để truyền tham số cho lớp ẩn danh?


146

Có thể truyền tham số hoặc truy cập tham số bên ngoài vào một lớp ẩn danh? Ví dụ:

int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // How would one access myVariable here?
    }
});

Có cách nào để người nghe truy cập myVariable hoặc được thông qua myVariable mà không tạo ra trình nghe như một lớp có tên thực tế không?


7
Bạn có thể tham chiếu finalcác biến cục bộ từ phương thức kèm theo.
Tom Hawtin - tackline

Tôi thực sự thích giao diện của Adam Mmlodzinski về việc xác định một phương thức riêng khởi tạo (các) cá thể myVariable riêng tư và có thể được gọi tại dấu ngoặc cuối do quay trở lại this.
dlamblin

Câu hỏi này có một số mục tiêu được chia sẻ là: stackoverflow.com/questions/362424/ từ
Alastair McCormack

Bạn cũng có thể sử dụng các biến lớp toàn cầu từ bên trong lớp ẩn danh. Có lẽ không sạch sẽ lắm, nhưng nó có thể làm được việc.
Jori

Câu trả lời:


78

Về mặt kỹ thuật, không, bởi vì các lớp ẩn danh không thể có các hàm tạo.

Tuy nhiên, các lớp có thể tham chiếu các biến từ việc chứa phạm vi. Đối với một lớp ẩn danh, đây có thể là các biến thể hiện từ (các) lớp chứa hoặc các biến cục bộ được đánh dấu cuối cùng.

chỉnh sửa : Như Peter đã chỉ ra, bạn cũng có thể truyền tham số cho hàm tạo của siêu lớp của lớp ẩn danh.


21
Một lớp ẩn danh sử dụng các hàm tạo của cha mẹ của nó. ví dụnew ArrayList(10) { }
Peter Lawrey

Điểm tốt. Vì vậy, đó sẽ là một cách khác để truyền tham số cho một lớp ẩn danh, mặc dù có khả năng bạn sẽ không có quyền kiểm soát tham số đó.
Matthew Willis

các lớp ẩn danh không cần nhà xây dựng
newacct

4
Các lớp ẩn danh có thể có Trình khởi tạo sơ thẩm, có thể hoạt động như các hàm tạo không tham số trong các lớp ẩn danh. Chúng được thực hiện theo cùng thứ tự như các bài tập trường, tức là sau super()và trước phần còn lại của hàm tạo thực tế. new someclass(){ fields; {initializer} fields; methods(){} }. Nó giống như một trình khởi tạo tĩnh nhưng không có từ khóa tĩnh. docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6
Mark Jeronimus

Xem stackoverflow.com/a/3045185/1737819 này cho biết cách thực hiện mà không cần hàm tạo.
Nhà phát triển Marius ilėnas

336

Có, bằng cách thêm một phương thức khởi tạo trả về 'cái này' và gọi ngay phương thức đó:

int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    private int anonVar;
    public void actionPerformed(ActionEvent e) {
        // How would one access myVariable here?
        // It's now here:
        System.out.println("Initialized with value: " + anonVar);
    }
    private ActionListener init(int var){
        anonVar = var;
        return this;
    }
}.init(myVariable)  );

Không cần tuyên bố 'cuối cùng'.


4
wow ... rực rỡ! Tôi quá mệt mỏi với việc tạo một finalđối tượng tham chiếu chỉ để tôi có thể lấy thông tin vào các lớp ẩn danh của mình. Cảm ơn bạn đã chia sẻ!
Matt Klein

7
Tại sao init()chức năng phải trả về this? Tôi không nhận được cú pháp thực sự.
Jori

11
bởi vì myButton.addActionListener (...) của bạn mong đợi một đối tượng ActionListener là một đối tượng được trả về khi bạn gọi phương thức của nó.

1
Tôi đoán .. tôi thấy rằng bản thân mình khá xấu xí, mặc dù nó hoạt động. Tôi thấy hầu hết thời gian tôi có thể đơn giản đủ khả năng để làm cho các biến và tham số hàm cần thiết cuối cùng và trực tiếp tham chiếu chúng từ lớp bên trong, vì thông thường chúng chỉ được đọc.
Thomas

2
Đơn giản hơn: private int anonVar = myVariable;
Anm

29

Đúng. bạn có thể chụp biến, hiển thị cho lớp bên trong. hạn chế duy nhất là nó phải là cuối cùng


Các biến sơ thẩm được tham chiếu từ một lớp ẩn danh không phải là afaik cuối cùng.
Matthew Willis

8
Các biến sơ thẩm được tham chiếu thông qua thisđó là cuối cùng.
Peter Lawrey

Điều gì xảy ra nếu tôi không muốn biến đổi thành final? Tôi không thể tìm thấy bất kỳ thay thế. Điều này có thể ảnh hưởng đến tham số gốc được thiết kế final.
Alston

20

Như thế này:

final int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // Now you can access it alright.
    }
});

14

Điều này sẽ làm nên điều kỳ diệu

int myVariable = 1;

myButton.addActionListener(new ActionListener() {

    int myVariable;

    public void actionPerformed(ActionEvent e) {
        // myVariable ...
    }

    public ActionListener setParams(int myVariable) {

        this.myVariable = myVariable;

        return this;
    }
}.setParams(myVariable));

8

Như được hiển thị tại http://www.coderanch.com/t/567294/java/java/declare-constructor-anonymous- class bạn có thể thêm một trình khởi tạo cá thể. Đó là một khối không có tên và được thực thi trước tiên (giống như một hàm tạo).

Có vẻ như họ cũng đã thảo luận tại sao Khởi tạo sơ thẩm java? một trình khởi tạo cá thể khác với một hàm tạo như thế nào? thảo luận về sự khác biệt từ các nhà xây dựng.


Điều này không giải quyết được câu hỏi đang được hỏi. Bạn vẫn sẽ gặp vấn đề khi truy cập các biến cục bộ, vì vậy bạn sẽ cần sử dụng giải pháp từ Adam Mlodzinski hoặc adarshr
Matt Klein

1
@MattKlein Với tôi, có vẻ như nó giải quyết được nó. Đó là điều tương tự thực sự, và ít dài dòng hơn.
haelix

1
Câu hỏi muốn biết làm thế nào để truyền tham số vào lớp, giống như bạn làm với một hàm tạo yêu cầu tham số. Liên kết (cần được tóm tắt ở đây) chỉ cho thấy làm thế nào để có một trình khởi tạo cá thể không có tham số, không trả lời câu hỏi. Kỹ thuật này có thể được sử dụng với finalcác biến như được mô tả bởi aav, nhưng thông tin đó không được cung cấp trong câu trả lời này. Cho đến nay, câu trả lời tốt nhất là câu trả lời được đưa ra bởi Adam Mlodzinksi (Bây giờ tôi chỉ sử dụng mô hình này, không có thêm trận chung kết!). Tôi đứng trước bình luận của mình rằng điều này không trả lời câu hỏi.
Matt Klein

7

Giải pháp của tôi là sử dụng một phương thức trả về lớp ẩn danh đã triển khai. Các đối số thông thường có thể được truyền cho phương thức và có sẵn trong lớp ẩn danh.

Ví dụ: (từ một số mã GWT để xử lý thay đổi hộp Văn bản):

/* Regular method. Returns the required interface/abstract/class
   Arguments are defined as final */
private ChangeHandler newNameChangeHandler(final String axisId, final Logger logger) {

    // Return a new anonymous class
    return new ChangeHandler() {
        public void onChange(ChangeEvent event) {
            // Access method scope variables           
            logger.fine(axisId)
        }
     };
}

Trong ví dụ này, phương thức lớp ẩn danh mới sẽ được tham chiếu với:

textBox.addChangeHandler(newNameChangeHandler(myAxisName, myLogger))

HOẶC , sử dụng các yêu cầu của OP:

private ActionListener newActionListener(final int aVariable) {
    return new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println("Your variable is: " + aVariable);
        }
    };
}
...
int myVariable = 1;
newActionListener(myVariable);

Điều này là tốt, nó giới hạn lớp ẩn danh ở một vài biến dễ xác định và loại bỏ những điều đáng ghê tởm khi phải thực hiện một số biến cuối cùng.
Biến khốn khổ

3

Những người khác đã trả lời rằng các lớp ẩn danh chỉ có thể truy cập các biến cuối cùng. Nhưng họ bỏ ngỏ câu hỏi làm thế nào để giữ cho biến ban đầu không phải là cuối cùng. Adam Mlodzinski đã đưa ra một giải pháp nhưng khá là bồng bềnh . Có một giải pháp đơn giản hơn nhiều cho vấn đề:

Nếu bạn không muốn myVariablelà người cuối cùng, bạn phải bọc nó trong một phạm vi mới, nơi nó không quan trọng, nếu nó là cuối cùng.

int myVariable = 1;

{
    final int anonVar = myVariable;

    myButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // How would one access myVariable here?
            // Use anonVar instead of myVariable
        }
    });
}

Adam Mlodzinski không làm gì khác trong câu trả lời của mình mà với nhiều mã hơn.


Điều này vẫn hoạt động mà không có phạm vi bổ sung. Nó thực sự giống như các câu trả lời khác sử dụng cuối cùng.
Adam Mlodzinski

@AdamMlodzinski Không, nó thực sự giống như câu trả lời của bạn, bởi vì nó giới thiệu một biến mới với giá trị của biến ban đầu trong phạm vi riêng tư.
ceving

Nó không thực sự giống nhau. Trong trường hợp của bạn, lớp bên trong của bạn không thể thay đổi anonVar - do đó, hiệu ứng là khác nhau. Nếu lớp bên trong của bạn phải, nói, duy trì một số trạng thái, mã của bạn sẽ phải sử dụng một số loại Object với một setter chứ không phải là nguyên thủy.
Adam Mlodzinski

@AdamMlodzinski Đó không phải là câu hỏi. Câu hỏi là làm thế nào để truy cập vào biến ngoài mà không làm cho nó cuối cùng. Và giải pháp là tạo một bản sao cuối cùng. Và tất nhiên, rõ ràng là người ta có thể tạo một bản sao có thể thay đổi bổ sung của biến trong trình nghe. Nhưng thứ nhất nó không được hỏi và thứ hai nó không yêu cầu bất kỳ initphương pháp nào . Tôi có thể thêm một dòng mã bổ sung vào ví dụ của mình để có biến bổ sung này. Nếu bạn là một fan hâm mộ lớn của các mẫu xây dựng, hãy sử dụng chúng, nhưng chúng không cần thiết trong trường hợp này.
ceving

Tôi không thấy điều này khác với việc sử dụng finalgiải pháp thay đổi.
Kevin Rave

3

Bạn có thể sử dụng lambdas đơn giản ("biểu thức lambda có thể nắm bắt các biến")

int myVariable = 1;
ActionListener al = ae->System.out.println(myVariable);
myButton.addActionListener( al );

hoặc thậm chí là một chức năng

Function<Integer,ActionListener> printInt = 
    intvar -> ae -> System.out.println(intvar);

int myVariable = 1;
myButton.addActionListener( printInt.apply(myVariable) );

Sử dụng chức năng là một cách tuyệt vời để cấu trúc lại trang trí và bộ điều hợp, xem tại đây

Tôi mới bắt đầu tìm hiểu về lambdas, vì vậy nếu bạn phát hiện ra một lỗi, hãy viết bình luận.


1

Một cách đơn giản để đặt một số giá trị vào một biến bên ngoài (không thuộc về lớp ẩn danh) là cách folow!

Theo cách tương tự nếu bạn muốn lấy giá trị của biến ngoài, bạn có thể tạo một phương thức trả về những gì bạn muốn!

public class Example{

    private TypeParameter parameter;

    private void setMethod(TypeParameter parameter){

        this.parameter = parameter;

    }

    //...
    //into the anonymus class
    new AnonymusClass(){

        final TypeParameter parameterFinal = something;
        //you can call setMethod(TypeParameter parameter) here and pass the
        //parameterFinal
        setMethod(parameterFinal); 

        //now the variable out the class anonymus has the value of
        //of parameterFinal

    });

 }

-2

Tôi nghĩ rằng các lớp ẩn danh về cơ bản giống như lambdas nhưng với cú pháp tệ hơn ... điều này hóa ra là đúng nhưng cú pháp thậm chí còn tồi tệ hơn và khiến các biến cục bộ (nên là gì) chảy vào lớp chứa.

Bạn có thể truy cập không có biến cuối cùng bằng cách biến chúng thành các trường của lớp cha.

Ví dụ

Giao diện:

public interface TextProcessor
{
    public String Process(String text);
}

lớp học:

private String _key;

public String toJson()
{
    TextProcessor textProcessor = new TextProcessor() {
        @Override
        public String Process(String text)
        {
            return _key + ":" + text;
        }
    };

    JSONTypeProcessor typeProcessor = new JSONTypeProcessor(textProcessor);

    foreach(String key : keys)
    {
        _key = key;

        typeProcessor.doStuffThatUsesLambda();
    }

Tôi không biết họ đã sắp xếp thứ này trong java 8 chưa (tôi bị mắc kẹt trong thế giới EE và chưa có 8) nhưng trong C # thì nó sẽ như thế này:

    public string ToJson()
    {
        string key = null;
        var typeProcessor = new JSONTypeProcessor(text => key + ":" + text);

        foreach (var theKey in keys)
        {
            key = theKey;

            typeProcessor.doStuffThatUsesLambda();
        }
    }

Bạn không cần một giao diện riêng trong c # hoặc ... Tôi nhớ nó! Tôi thấy mình tạo ra các thiết kế tồi tệ hơn trong java và lặp đi lặp lại nhiều hơn bởi vì số lượng mã + độ phức tạp bạn phải thêm vào java để sử dụng lại một cái gì đó còn tệ hơn là chỉ sao chép và dán nhiều thời gian.


Trông giống như một hack khác mà bạn có thể sử dụng là có một mảng một phần tử như được đề cập ở đây stackoverflow.com/a/4732586/962696
JonnyRaa
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.