Как передать параметры анонимному классу? - PullRequest
140 голосов
/ 24 февраля 2011

Можно ли передать параметры или получить доступ к внешним параметрам анонимному классу? Например:

int myVariable = 1;

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

Есть ли способ для слушателя получить доступ к myVariable или быть переданным myVariable без создания слушателя как фактического именованного класса?

Ответы [ 11 ]

328 голосов
/ 31 августа 2012

Да, добавив метод инициализатора, который возвращает 'this', и немедленно вызвав этот метод:

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

Не требуется окончательное объявление.

75 голосов
/ 24 февраля 2011

Технически, нет, потому что анонимные классы не могут иметь конструкторов.

Однако классы могут ссылаться на переменные из содержащих области.Для анонимного класса это могут быть переменные экземпляра из содержащего класса (классов) или локальные переменные, помеченные как окончательные.

edit : Как отметил Питер, вы также можете передавать параметры вконструктор суперкласса анонимного класса.

27 голосов
/ 24 февраля 2011

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

20 голосов
/ 24 февраля 2011

Как это:

final int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // Now you can access it alright.
    }
});
13 голосов
/ 18 декабря 2014

Это сделает волшебство

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 голосов
/ 18 февраля 2013

Как показано на http://www.coderanch.com/t/567294/java/java/declare-constructor-anonymous-class, вы можете добавить инициализатор экземпляра.Это блок, который не имеет имени и выполняется первым (точно так же, как конструктор).

Похоже, они также обсуждались в Почему инициализаторы java-экземпляров? и Чем инициализатор экземпляра отличается от конструктора? обсуждает отличия от конструкторов.

7 голосов
/ 18 апреля 2013

Мое решение - использовать метод, который возвращает реализованный анонимный класс.Обычные аргументы могут быть переданы методу и доступны в анонимном классе.

Например: (из некоторого кода GWT для обработки изменения текстового поля):

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

Для этого примерана новый анонимный метод класса будет ссылаться:

textBox.addChangeHandler(newNameChangeHandler(myAxisName, myLogger))

ИЛИ , используя требования ОП:

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);
3 голосов
/ 15 марта 2017

Вы можете использовать обычные лямбды ("лямбда-выражения могут захватывать переменные")

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

или даже функцию

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

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

Использование функции - отличное решениеспособ рефакторинга декораторов и адаптеров, см. здесь

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

3 голосов
/ 30 октября 2013

Другие люди уже ответили, что анонимные классы могут обращаться только к конечным переменным.Но они оставляют открытым вопрос, как сохранить исходную переменную не окончательной. Адам Млодзинский дал решение, но это довольно раздутый.Существует гораздо более простое решение проблемы:

Если вы не хотите, чтобы myVariable было окончательным, вы должны заключить его в новую область, где это не имеет значения, если оно окончательно.

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

Адам Млодзинский больше ничего не делает в своем ответе, но с гораздо большим количеством кода.

1 голос
/ 04 июля 2014

Простой способ поместить какое-либо значение во внешнюю переменную (не принадлежит классу anonymus) - вот как!

Таким же образом, если вы хотите получить значение внешней переменной, вы можете создать метод, который возвращает то, что вы хотите!

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

    });

 }
...