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?
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.
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.
yes. you can capture variable, visible to the inner class. the only limitation is that it has to be final
Like this:
final int myVariable = 1;
myButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Now you can access it alright.
}
});
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));
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.
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);
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.
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.
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
});
}
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;
}
});
}
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.
精彩评论