Lewis
Lewis

Reputation: 3415

How to pass parameters to anonymous class?

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?

Upvotes: 157

Views: 102863

Answers (12)

pasaba por aqui
pasaba por aqui

Reputation: 3529

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

Upvotes: 1

ZiglioUK
ZiglioUK

Reputation: 2610

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.

Upvotes: 3

user3510955
user3510955

Reputation:

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

Upvotes: 14

JonnyRaa
JonnyRaa

Reputation: 8038

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.

Upvotes: -2

heronsanches
heronsanches

Reputation: 596

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

    });

 }

Upvotes: 1

Alastair McCormack
Alastair McCormack

Reputation: 27704

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

Upvotes: 7

ceving
ceving

Reputation: 23814

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.

Upvotes: 3

Rob Russell
Rob Russell

Reputation: 472

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.

Upvotes: 8

Adam Mlodzinski
Adam Mlodzinski

Reputation: 3782

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.

Upvotes: 358

Matthew
Matthew

Reputation: 44919

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.

Upvotes: 81

adarshr
adarshr

Reputation: 62583

Like this:

final int myVariable = 1;

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

Upvotes: 22

aav
aav

Reputation: 2489

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

Upvotes: 31

Related Questions