154

Is it possible to pass parameters, or access external parameters to an anonymous class? For example:

int myVariable = 1;

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

Is there any way for the listener to access myVariable or be passed myVariable without creating the listener as an actual named class?

Lewis
  • 3,375
  • 3
  • 29
  • 26
  • 8
    You can reference `final` local variables from the enclosing method. – Tom Hawtin - tackline Feb 24 '11 at 16:05
  • I do like the look of Adam Mmlodzinski's suggestion of defining a private method that initializes private myVariable instance(s) and can be called at the closing brace due to returning `this`. – dlamblin Feb 21 '13 at 04:22
  • This question has some shared goals of: http://stackoverflow.com/questions/362424/accessing-constructor-of-an-anonymous-class – Alastair McCormack Apr 18 '13 at 09:20
  • You can also use the global class variables from inside the anonymous class. Perhaps not very clean, but it can do the job. – Jori Jul 18 '13 at 19:37

12 Answers12

355

Yes, by adding an initializer method that returns 'this', and immediately calling that method:

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

No 'final' declaration needed.

Adam Mlodzinski
  • 3,728
  • 2
  • 13
  • 10
  • This is pretty great! Say the interface is returning anonVar somewhere though, and anonVar were maybe a collection, it'd be nice if you could set it as final, but then you'd be back at the other way to do this. – dlamblin Feb 21 '13 at 04:20
  • dlamblin, if anonVar were a Collection, declaring it final wouldn't protect the contents if you expose it with a getter method. Rather, your getter method would need to either return a copy of the Collection, or wrap it using one of the Collections.unmodifiableCollection() variants. If you're suggesting to set it final as one might do with captured constructor arguments, then, no, you can't do that - you would need to make a named class instead. – Adam Mlodzinski Apr 10 '13 at 04:34
  • 5
    wow... brilliant! I'm so tired of creating a `final` reference object just so I can get info into my anonymous classes. Thank you for sharing! – Matt Klein Apr 17 '13 at 21:30
  • 7
    Why does the `init()` function have to return `this`? I don't get the syntax really. – Jori Jul 22 '13 at 18:04
  • 12
    because your myButton.addActionListener(...) expects an ActionListener object as an object which is returned when you call its method. –  Aug 09 '13 at 21:57
  • 1
    I guess.. I find that rather ugly myself though, though it works. I find most of the time I can simply afford to make the necessary variables and function parameters final and directly reference them from the inner class, since usually they are only getting read. – Thomas Sep 27 '13 at 02:12
  • The problem with this solution is, that the initialization takes place out of the constructor and this means, that the variables to be initialized (here `anonVar`) can not be final any more. This approach works for all but final variables. – ceving Oct 30 '13 at 16:22
  • 1
    ceving, how is that a problem? If you want to initialzie a final variable, there is no need to add it to your anonymous class - you can use (or add, as you do in your answer below) a final variable declared outside of the anonymous class. If, however, your anonymous class extends some other class which initializes some final variables, you would have to pass those to the constructor anyway. – Adam Mlodzinski Oct 31 '13 at 00:48
  • 1
    Why is it, `init()` can be private? – phant0m May 24 '14 at 21:54
  • it reminds me of javascript :) Good solution though. – Breno Inojosa Jun 19 '14 at 07:25
  • 2
    Simpler: private int anonVar = myVariable; – Anm Jul 25 '14 at 20:17
81

Technically, no, because anonymous classes can't have constructors.

However, classes can reference variables from containing scopes. For an anonymous class these can be instance variables from the containing class(es) or local variables that are marked final.

edit: As Peter pointed out, you can also pass parameters to the constructor of the superclass of the anonymous class.

Matthew
  • 44,826
  • 10
  • 98
  • 87
  • 21
    An anonymous class use use the constructors of its parent. e.g `new ArrayList(10) { }` – Peter Lawrey Feb 24 '11 at 17:08
  • Good point. So that would be another way to pass a parameter to an anonymous class, although it is likely you won't have control over that parameter. – Matthew Feb 24 '11 at 17:50
  • anonymous classes don't need constructors – newacct Jul 28 '12 at 08:47
  • 4
    Anonymous classes can have Instance Initializers, which can function as parameter-less constructors in anonymous classes. These are executed in the same order as field assignments, i.e. after `super()` and before the rest of the actual constructor. `new someclass(){ fields; {initializer} fields; methods(){} }`. It's sort of like a static initializer but without the static keyword. http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6 – Mark Jeronimus Sep 23 '14 at 16:44
  • See this http://stackoverflow.com/a/3045185/1737819 it says how to implement without constructor. – Developer Marius Žilėnas Jul 02 '15 at 12:16
31

yes. you can capture variable, visible to the inner class. the only limitation is that it has to be final

aav
  • 2,514
  • 1
  • 19
  • 27
  • Instance variables referenced from an anonymous class do not have to be final afaik. – Matthew Feb 24 '11 at 16:49
  • 8
    Instance variables are referenced via `this` which is final. – Peter Lawrey Feb 24 '11 at 17:04
  • What if I don't want the variable be changed to `final`? I can't find any alternative. This may influence the origin parameter which is designed to be `final`. – Alston Sep 18 '14 at 08:28
22

Like this:

final int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // Now you can access it alright.
    }
});
adarshr
  • 61,315
  • 23
  • 138
  • 167
14

This will do the magic

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

As shown at http://www.coderanch.com/t/567294/java/java/declare-constructor-anonymous-class you can add an instance initializer. It's a block that doesn't have a name and gets executed first (just like a constructor).

Looks like they're also discussed at Why java Instance initializers? and How is an instance initializer different from a constructor? discusses differences from constructors.

Community
  • 1
  • 1
Rob Russell
  • 462
  • 3
  • 14
  • This doesn't solve the question being asked. You will still have the problem of accessing local variables, so you'll either need to use the solution from Adam Mlodzinski or adarshr – Matt Klein Apr 17 '13 at 22:21
  • 1
    @MattKlein To me, it looks like it solves it. It's the same thing actually, and less verbose. – haelix Aug 29 '13 at 08:50
  • 1
    The question wanted to know how to pass parameters into the class, like you would with a constructor that requires parameters. The link (which should have been summarized here) only shows how to have a parameter-less instance initializer, which doesn't answer the question. This technique could be used with `final` variables as described by aav, but that info wasn't provided in this answer. By far, the best answer is the one given by Adam Mlodzinksi (I now use this pattern exclusively, no more finals!). I stand by my comment that this doesn't answer the question asked. – Matt Klein Aug 29 '13 at 14:54
7

My solution is to use a method that returns the implemented anonymous class. Regular arguments may be passed to the method and are available within the anonymous class.

For example: (from some GWT code to handle a Text box change):

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

For this example, the new anonymous class-method would be referenced with:

textBox.addChangeHandler(newNameChangeHandler(myAxisName, myLogger))

OR, using the OP's requirements:

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);
Alastair McCormack
  • 26,573
  • 8
  • 77
  • 100
  • This is good, it restricts the anonymous class to an few easy-to-identify variables and removes abominations of having to make some variables final. – Miserable Variable Oct 30 '13 at 16:45
3

Other people have already answered that anonymous classes can access only final variables. But they leave the question open how to keep the original variable non-final. Adam Mlodzinski gave a solution but is is pretty bloated. There is a much simpler solution for the problem:

If you do not want myVariable to be final you have to wrap it in a new scope where it does not matter, if it is final.

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 does not do anything else in his answer but with much more code.

Community
  • 1
  • 1
ceving
  • 21,900
  • 13
  • 104
  • 178
  • This still works without the extra scope. It's effectively the same as the other answers using final. – Adam Mlodzinski Oct 31 '13 at 00:34
  • @AdamMlodzinski No it is effectively the same as your answer, because it introduces a new variable with the value of the original variable in a private scope. – ceving Nov 02 '13 at 21:33
  • It is not effectively the same. In your case, your inner class cannot make changes to anonVar - thus, the effect is different. If your inner class has to, say, maintain some state, your code would have to use some sort of Object with a setter rather than a primitive. – Adam Mlodzinski Nov 13 '13 at 18:43
  • @AdamMlodzinski That was not the question. The question was how to access the outer variable without making itself final. And the solution is to make a final copy. And of course it is obvious that one can make an additional mutable copy of the variable in the listener. But first it was not asked and second it does not require any `init` method. I can add one additional line of code to my example in order to have this additional variable. If you are a great fan of builder patterns feel free to use them, but they are not necessary in this case. – ceving Nov 21 '13 at 18:21
  • I don't see how this is different than using `final` variable solution. – Kevin Rave Dec 11 '13 at 04:16
3

You can use plain lambdas ("lambda expressions can capture variables")

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

or even a Function

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

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

Using Function is a great way to refactor Decorators and Adapters, see here

I've just started learning about lambdas, so if you spot a mistake, feel free to write a comment.

ZiglioUK
  • 2,573
  • 4
  • 27
  • 32
1

A simple way for put some value into a external variable(doesn't belong for anonymus class) is how folow!

In the same way if you want get the value of a external variable you can create a method that return what you want!

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

    });

 }
heronsanches
  • 524
  • 7
  • 12
1

If "myVariable" is a field, you can use a qualified this:

public class Foo {
    int myVariable = 1;

    new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            Foo.this.myVariable = 8;
        }
    });
}
pasaba por aqui
  • 3,446
  • 16
  • 40
-2

I thought anonymous classes were basically like lambdas but with worse syntax... this turns out to be true but the syntax is even worse and causes (what should be) local variables to bleed out into the containing class.

You can access none final variables by making them into fields of the parent class.

Eg

Interface:

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

class:

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

I dont know if they've sorted this out in java 8 (I'm stuck in EE world and not got 8 yet) but in C# it would look like this:

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

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

            typeProcessor.doStuffThatUsesLambda();
        }
    }

You dont need a seperate interface in c# either... I miss it! I find myself making worse designs in java and repeating myself more because the amount of code + complexity you have to add in java to reuse something is worse than just copy and pasting a lot of the time.

JonnyRaa
  • 7,559
  • 6
  • 45
  • 49
  • looks like another hack you can use is to have a one element array as mentioned here http://stackoverflow.com/a/4732586/962696 – JonnyRaa Sep 09 '14 at 14:08