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

Възможно ли е да се подават параметри или да се осъществява достъп до външни параметри към анонимен клас? Например:

int myVariable = 1;

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

Има ли някакъв начин слушателят да получи достъп до myVariable или да му бъде предадена myVariable, без да създава слушателя като действително именуван клас?


person Lewis    schedule 24.02.2011    source източник
comment
Можете да препратите към final локални променливи от обхващащия метод.   -  person Tom Hawtin - tackline    schedule 24.02.2011
comment
Харесва ми вида на предложението на Adam Mmlodzinski за дефиниране на частен метод, който инициализира частен екземпляр(и) на myVariable и може да бъде извикан в затварящата скоба поради връщането на this.   -  person dlamblin    schedule 21.02.2013
comment
Този въпрос има някои общи цели от: stackoverflow. com/questions/362424/   -  person Alastair McCormack    schedule 18.04.2013
comment
Можете също да използвате променливите на глобалния клас от вътрешността на анонимния клас. Може би не е много чисто, но може да свърши работа.   -  person Jori    schedule 18.07.2013


Отговори (11)


Технически не, защото анонимните класове не могат да имат конструктори.

Класовете обаче могат да препращат към променливи от съдържащи обхвати. За анонимен клас това могат да бъдат променливи на екземпляр от съдържащия клас(ове) или локални променливи, които са маркирани като окончателни.

редактиране: Както Питър посочи, можете също да подадете параметри към конструктора на суперкласа на анонимния клас.

person Matthew Willis    schedule 24.02.2011
comment
Използването на анонимен клас използва конструкторите на своя родител. напр. new ArrayList(10) { } - person Peter Lawrey; 24.02.2011
comment
Добра точка. Така че това би бил друг начин за предаване на параметър към анонимен клас, въпреки че е вероятно да нямате контрол върху този параметър. - person Matthew Willis; 24.02.2011
comment
анонимните класове не се нуждаят от конструктори - person newacct; 28.07.2012
comment
Анонимните класове могат да имат инициализатори на екземпляри, които могат да функционират като конструктори без параметри в анонимни класове. Те се изпълняват в същия ред като присвояването на полета, т.е. след super() и преди останалата част от действителния конструктор. new someclass(){ fields; {initializer} fields; methods(){} }. Това е нещо като статичен инициализатор, но без ключовата дума static. docs.oracle.com/javase/ specs/jls/se7/html/jls-8.html#jls-8.6 - person Mark Jeronimus; 23.09.2014
comment
Вижте това stackoverflow.com/a/3045185/1737819, в което се казва как да се внедри без конструктор. - person Developer Marius Žilėnas; 02.07.2015

Да, чрез добавяне на метод за инициализиране, който връща „това“, и незабавно извикване на този метод:

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

Не е необходима „окончателна“ декларация.

person Adam Mlodzinski    schedule 30.08.2012
comment

При търсене на „XML-less Spring конфигурация“ или Конфигурация на Spring, базирана на Java вероятно би трябвало да я разберете сами в рамките на няколко минути.

Не сте дефинирали никакъв метод за създаване на bean, който се хоства от клас, който е анотиран с @Configuration - така че Spring може да знае какво всъщност искате да инжектирате.

@Configuration
// optional @ComponentScan(basePackages = {"name.your.package.here"}
public class AppConfig
{
    @Bean // or @Bean(name = "nameOfYourBean")
    public ResourcePool getResourcePool() 
    { 
        return new ResourcePool(); 
    }
}

В рамките на вашия основен метод след това трябва да създадете Spring контекст и да заредите вашия AppConfig във вашия контекст:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfic.class);

HTH

- person dlamblin; 21.02.2013
comment
dlamblin, ако anonVar беше колекция, декларирането й като final няма да защити съдържанието, ако го изложите с метод getter. По-скоро вашият метод за получаване ще трябва или да върне копие на колекцията, или да я обвие с помощта на един от вариантите на Collections.unmodifiableCollection(). Ако предлагате да го зададете окончателно, както може да се направи със захванати аргументи на конструктора, тогава не, не можете да направите това - вместо това ще трябва да създадете именуван клас. - person Adam Mlodzinski; 10.04.2013
comment
уау... брилянтно! Толкова съм уморен да създавам final референтен обект само за да мога да получа информация в моите анонимни класове. Благодаря за споделянето! - person Matt Klein; 18.04.2013
comment
Защо функцията init() трябва да върне this? Наистина не разбирам синтаксиса. - person Jori; 22.07.2013
comment
защото вашият myButton.addActionListener(...) очаква обект ActionListener като обект, който се връща, когато извикате неговия метод. - person ; 10.08.2013
comment
Страхотен... [само пълнител] - person ATOzTOA; 11.09.2013
comment
Предполагам.. Аз самият намирам това за доста грозно, въпреки че работи. Намирам, че през повечето време мога просто да си позволя да направя необходимите променливи и функционални параметри окончателни и директно да ги препращам от вътрешния клас, тъй като обикновено те само се четат. - person Thomas; 27.09.2013
comment
Проблемът с това решение е, че инициализацията се извършва извън конструктора и това означава, че променливите, които трябва да се инициализират (тук anonVar), вече не могат да бъдат окончателни. Този подход работи за всички променливи, освен за крайните. - person ceving; 30.10.2013
comment
ceving, как е това проблем? Ако искате да инициализирате крайна променлива, няма нужда да я добавяте към вашия анонимен клас - можете да използвате (или да добавите, както правите в отговора си по-долу) крайна променлива, декларирана извън анонимния клас. Ако, обаче, вашият анонимен клас разширява някой друг клас, който инициализира някои крайни променливи, ще трябва да ги предадете на конструктора така или иначе. - person Adam Mlodzinski; 31.10.2013
comment
Защо, init() може да е лично? - person phant0m; 25.05.2014
comment
напомня ми на javascript :) Все пак добро решение. - person Breno Inojosa; 19.06.2014
comment
По-просто: private int anonVar = myVariable; - person Anm; 26.07.2014
comment
Това се нуждае от лепкав - person Richard; 27.09.2017

да можете да уловите променлива, видима за вътрешния клас. единственото ограничение е, че трябва да е окончателен

person aav    schedule 24.02.2011
comment
Променливите на екземпляра, посочени от анонимен клас, не трябва да са окончателни afaik. - person Matthew Willis; 24.02.2011
comment
Променливите на екземпляра се препращат чрез this, което е окончателно. - person Peter Lawrey; 24.02.2011
comment
Какво ще стане, ако не искам променливата да бъде променена на final? Не намирам алтернатива. Това може да повлияе на параметъра за произход, който е проектиран да бъде final. - person Alston; 18.09.2014

Като този:

final int myVariable = 1;

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

Това ще направи магията

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));
person Community    schedule 18.12.2014

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

Изглежда, че те също се обсъждат в Защо инициализатори на екземпляри на Java? и Как се различава инициализаторът на екземпляр от конструктор? обсъжда разликите от конструкторите.

person Rob Russell    schedule 18.02.2013
comment
Това не решава зададения въпрос. Все още ще имате проблем с достъпа до локални променливи, така че или ще трябва да използвате решението от Adam Mlodzinski или adarsh - person Matt Klein; 18.04.2013
comment
@MattKlein За мен изглежда, че го решава. Това всъщност е същото и по-малко многословно. - person haelix; 29.08.2013
comment
Въпросът искаше да знае как да предава параметри в класа, както бихте направили с конструктор, който изисква параметри. Връзката (която трябваше да бъде обобщена тук) само показва как да имате инициализатор на екземпляр без параметър, което не отговаря на въпроса. Тази техника може да се използва с final променливи, както е описано от aav, но тази информация не е предоставена в този отговор. Досега най-добрият отговор е този, даден от Адам Млодзинкси (сега използвам изключително този модел, без повече финали!). Поддържам коментара си, че това не отговаря на зададения въпрос. - person Matt Klein; 29.08.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))

ИЛИ, използвайки изискванията на 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);
person Alastair McCormack    schedule 18.04.2013
comment
Това е добре, ограничава анонимния клас до няколко лесни за идентифициране променливи и премахва отвратителните неща да се правят някои променливи окончателни. - person Miserable Variable; 30.10.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
        }
    });
}

Адам Млодзински не прави нищо друго в своя отговор, освен с много повече код.

person ceving    schedule 30.10.2013
comment
Това все още работи без допълнителен обхват. На практика е същият като другите отговори, използвайки final. - person Adam Mlodzinski; 31.10.2013
comment
@AdamMlodzinski Не, всъщност е същият като вашия отговор, защото въвежда нова променлива със стойността на оригиналната променлива в частен обхват. - person ceving; 03.11.2013
comment
Ефективно не е същото. Във вашия случай вашият вътрешен клас не може да прави промени в anonVar - следователно ефектът е различен. Ако вашият вътрешен клас трябва, да речем, да поддържа някакво състояние, вашият код ще трябва да използва някакъв вид обект със сетер, а не примитивен. - person Adam Mlodzinski; 13.11.2013
comment
@AdamMlodzinski Това не беше въпросът. Въпросът беше как да получите достъп до външната променлива, без тя да стане окончателна. И решението е да направите окончателно копие. И разбира се, очевидно е, че може да се направи допълнително променящо се копие на променливата в слушателя. Но първо не е поискано и второ не изисква никакъв init метод. Мога да добавя един допълнителен ред код към моя пример, за да имам тази допълнителна променлива. Ако сте голям фен на шаблоните за строители, можете да ги използвате, но в този случай те не са необходими. - person ceving; 21.11.2013
comment
Не виждам как това е различно от използването на final променливо решение. - person Kevin Rave; 11.12.2013

Можете да използвате обикновени ламбда ( "ламбда изразите могат да улавят променливи")

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

Използването на функция е чудесен начин за рефакторинг на декоратори и адаптери, вижте тук

Току-що започнах да уча за ламбда, така че ако забележите грешка, не се колебайте да напишете коментар.

person ZiglioUK    schedule 14.03.2017

Прост начин за поставяне на някаква стойност във външна променлива (не принадлежи към анонимен клас) е как следва!

По същия начин, ако искате да получите стойността на външна променлива, можете да създадете метод, който връща това, което искате!

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

    });

 }
person heronsanches    schedule 03.07.2014

Мислех, че анонимните класове са основно като ламбда, но с по-лош синтаксис... това се оказва вярно, но синтаксисът е още по-лош и кара (какво трябва да бъде) локалните променливи да изтичат в съдържащия клас.

Можете да получите достъп до безкрайни променливи, като ги направите в полета на родителския клас.

Eg

Интерфейс:

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), но в C# ще изглежда така:

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

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

            typeProcessor.doStuffThatUsesLambda();
        }
    }

Вие също не се нуждаете от отделен интерфейс в c#... Липсва ми! Откривам, че правя по-лоши проекти в java и се повтарям повече, защото количеството код + сложността, което трябва да добавите в java, за да използвате повторно нещо, е по-лошо от просто копиране и поставяне през много време.

person JonnyRaa    schedule 09.09.2014
comment
изглежда, че друг хак, който можете да използвате, е да имате масив от един елемент, както е споменато тук stackoverflow.com/a/4732586/962696 - person JonnyRaa; 09.09.2014