How to pass parameters to anonymous class?

JavaAnonymous Class

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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.

Solution 2 - Java

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.

Solution 3 - Java

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

Solution 4 - Java

Like this:

final int myVariable = 1;

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

Solution 5 - Java

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

Solution 6 - Java

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 https://stackoverflow.com/questions/6763550/why-java-instance-initializers and https://stackoverflow.com/questions/1355810/how-is-an-instance-initializer-different-from-a-constructor discusses differences from constructors.

Solution 7 - Java

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

Solution 8 - Java

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.

Solution 9 - Java

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.

Solution 10 - Java

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

Solution 11 - Java

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

Solution 12 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLewisView Question on Stackoverflow
Solution 1 - JavaAdam MlodzinskiView Answer on Stackoverflow
Solution 2 - JavaMatthew WillisView Answer on Stackoverflow
Solution 3 - JavaaavView Answer on Stackoverflow
Solution 4 - JavaadarshrView Answer on Stackoverflow
Solution 5 - Javauser3510955View Answer on Stackoverflow
Solution 6 - JavaRob RussellView Answer on Stackoverflow
Solution 7 - JavaAlastair McCormackView Answer on Stackoverflow
Solution 8 - JavacevingView Answer on Stackoverflow
Solution 9 - JavaZiglioUKView Answer on Stackoverflow
Solution 10 - JavaheronsanchesView Answer on Stackoverflow
Solution 11 - Javapasaba por aquiView Answer on Stackoverflow
Solution 12 - JavaJonnyRaaView Answer on Stackoverflow