¿Cómo pasar parámetros a la clase anónima?

¿Es posible pasar parámetros o acceder a parámetros externos a una clase anónima? Por ejemplo:

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

¿Hay alguna forma para que el oyente acceda a myVariable o se pase myVariable sin crear el oyente como una clase con nombre real?

Técnicamente, no, porque las clases anónimas no pueden tener constructores.

Sin embargo, las clases pueden hacer referencia a variables que contengan ámbitos. Para una clase anónima, estas pueden ser variables de instancia de la (s) clase (s) contenedora (s) o variables locales que se marcan como finales.

editar : Como señaló Peter, también puede pasar parámetros al constructor de la clase superior de la clase anónima.

Sí, agregando un método de inicialización que devuelve ‘esto’, e inmediatamente llamando a ese método:

 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 se necesita una statement ‘final’.

sí. puedes capturar variables, visibles para la clase interna. la única limitación es que tiene que ser definitiva

Me gusta esto:

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

Esto hará la magia

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

Como se muestra en http://www.coderanch.com/t/567294/java/java/declare-constructor-anonymous-class , puede agregar un inicializador de instancia. Es un bloque que no tiene nombre y se ejecuta primero (como un constructor).

Parece que también se discuten en los inicializadores de instancias de java. y ¿cómo es un inicializador de instancia diferente de un constructor? discute las diferencias de los constructores.

Mi solución es usar un método que devuelva la clase anónima implementada. Se pueden pasar argumentos regulares al método y están disponibles dentro de la clase anónima.

Por ejemplo: (desde algún código GWT para manejar un cambio de cuadro de texto):

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

Para este ejemplo, se hace referencia al nuevo método de clase anónimo con:

 textBox.addChangeHandler(newNameChangeHandler(myAxisName, myLogger)) 

O , usando los requisitos de 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); 

Puedes usar lambdas simples (“expresiones lambda pueden capturar variables”)

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

o incluso una función

 Function printInt = intvar -> ae -> System.out.println(intvar); int myVariable = 1; myButton.addActionListener( printInt.apply(myVariable) ); 

El uso de la función es una excelente manera de refactorizar decoradores y adaptadores, mira aquí

Acabo de empezar a aprender sobre lambdas, por lo que si detecta un error, siéntase libre de escribir un comentario.

Otras personas ya han respondido que las clases anónimas solo pueden acceder a las variables finales. Pero dejan abierta la pregunta de cómo mantener la variable original no final. Adam Mlodzinski dio una solución, pero está bastante abotagado. Hay una solución mucho más simple para el problema:

Si no quiere que myVariable sea ​​definitivo, debe envolverlo en un nuevo ámbito en el que no importe, si es definitivo.

 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 no hace nada más en su respuesta, pero con mucho más código.

Una forma sencilla de poner un valor en una variable externa (no pertenece a la clase anonymus) es cómo folow!

De la misma manera, si quieres obtener el valor de una variable externa, puedes crear un método que devuelva lo que deseas.

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

Pensé que las clases anónimas eran básicamente como lambdas pero con peor syntax … esto resulta ser cierto, pero la syntax es aún peor y provoca (lo que debería ser) variables locales que se desvanezcan en la clase contenedora.

Puede acceder a ninguna variable final al convertirlas en campos de la clase principal.

P.ej

Interfaz:

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

clase:

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

No sé si han solucionado esto en Java 8 (estoy atrapado en el mundo EE y aún no tengo 8) pero en C # se vería así:

  public string ToJson() { string key = null; var typeProcessor = new JSONTypeProcessor(text => key + ":" + text); foreach (var theKey in keys) { key = theKey; typeProcessor.doStuffThatUsesLambda(); } } 

No necesita una interfaz separada en c # tampoco … ¡Lo extraño! Me encuentro haciendo peores diseños en Java y repitiéndome más porque la cantidad de código + complejidad que tienes que agregar en java para reutilizar algo es peor que simplemente copiar y pegar muchas veces.