Как передать параметры анонимному классу?

Можно ли передавать параметры или обращаться к внешним параметрам в анонимный класс? Например:

int myVariable = 1;

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

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

Ответ 1

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

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

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

Ответ 2

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

int myVariable = 1;

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

Не требуется декларация 'final'.

Ответ 3

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

Ответ 4

Вот так:

final int myVariable = 1;

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

Например: (из некоторого кода 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))

ИЛИ, используя требования 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);

Ответ 8

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

Если вы не хотите, чтобы 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
        }
    });
}

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

Ответ 9

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

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

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

    });

 }

Ответ 10

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

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

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

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

Ответ 11

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

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

Например,

Интерфейс:

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

Класс:

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

Я не знаю, отсортировали ли они это в java 8 (я застрял в мире EE и еще не получил 8), но в С# он будет выглядеть следующим образом:

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

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

            typeProcessor.doStuffThatUsesLambda();
        }
    }

Вам не нужен отдельный интерфейс в С#. Я скучаю по нему! Я нахожу, что делаю худшие проекты в java и повторяю себя больше, потому что количество кода + сложность, которую вы должны добавить в java для повторного использования, хуже, чем просто копировать и вставлять много времени.