2011-02-24 36 views
124

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

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 acceder myVariable o sea myVariable pasado sin crear la escucha como una clase llamada real?

+7

Puede hacer referencia a variables locales 'finales' desde el método adjunto. –

+0

Me gusta el aspecto de la sugerencia de Adam Mmlodzinski de definir un método privado que inicializa instancias privadas myVariable y se puede invocar en el corchete de cierre debido a la devolución de 'this'. – dlamblin

+0

Esta pregunta tiene algunos objetivos compartidos de: http://stackoverflow.com/questions/362424/accessing-constructor-of-an-anonymous-class –

Respuesta

68

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 superclase de la clase anónima.

+19

Un uso de clase anónimo usa los constructores de su elemento primario. por ejemplo 'new ArrayList (10) {}' –

+0

Buen punto. Entonces esa sería otra forma de pasar un parámetro a una clase anónima, aunque es probable que no tenga control sobre ese parámetro. –

+0

clases anónimas no necesitan constructores – newacct

22

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

+0

Las variables de instancia a las que se hace referencia desde una clase anónima no tienen que ser afaik finales. –

+8

Las variables de instancia se referencian a través de 'this' que es final. –

+0

¿Qué pasa si no quiero que la variable se cambie a 'final'?No puedo encontrar ninguna alternativa. Esto puede influir en el parámetro de origen que está diseñado para ser 'final'. – Stallman

19

De esta manera:

final int myVariable = 1; 

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

Sí, mediante la adición de un método de inicialización que devuelve 'esto', e inmediatamente llamar a ese método: declaración de 'final'

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 es necesario.

+0

¡Esto es genial! Supongamos que la interfaz está regresando de alguna parte a algún lugar, y anonVar quizás sea una colección, sería bueno si pudieras establecerla como definitiva, pero luego estarías de regreso en el otro lado para hacer esto. – dlamblin

+0

dlamblin, si anonVar fuera una Colección, declararla definitiva no protegería el contenido si lo expones con un método getter. Por el contrario, su método getter necesitaría devolver una copia de la colección o envolverla utilizando una de las variantes Collections.unmodifiableCollection(). Si está sugiriendo establecerlo final como podría hacerse con los argumentos del constructor capturado, entonces, no, no puede hacer eso; en su lugar, tendría que crear una clase con nombre. –

+4

wow ... ¡genial! Estoy tan cansado de crear un objeto de referencia 'final' solo para poder obtener información en mis clases anónimas. ¡Gracias por compartir! –

8

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 están discutidos en Why java Instance initializers? y How is an instance initializer different from a constructor? discute las diferencias de constructores.

+0

Esto no resuelve la pregunta que se hace. Todavía tendrá el problema de acceder a las variables locales, por lo que tendrá que usar la solución de Adam Mlodzinski o adarshr –

+1

@MattKlein Para mí, parece que lo resuelve. Es lo mismo en realidad, y menos detallado. – haelix

+1

La pregunta quería saber cómo pasar parámetros a la clase, como lo haría con un constructor que requiere parámetros. El enlace (que debería haberse resumido aquí) solo muestra cómo tener un inicializador de instancias sin parámetros, que no responde a la pregunta. Esta técnica podría usarse con las variables "finales" tal como se describe en aav, pero esa información no se proporcionó en esta respuesta. De lejos, la mejor respuesta es la dada por Adam Mlodzinksi (ahora uso este patrón exclusivamente, ¡no más finales!). Respaldo mi comentario de que esto no responde la pregunta formulada. –

6

Mi solución es utilizar 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: (a partir de un cierto código GWT para manejar un cambio 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 haría referencia a la nueva clase-método anónimo con:

textBox.addChangeHandler(newNameChangeHandler(myAxisName, myLogger)) 

O, utilizando 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); 
+0

Esto es bueno, restringe la clase anónima a unas pocas variables fáciles de identificar y elimina las abominaciones de tener que hacer que algunas variables sean definitivas. –

2

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 hinchado. Hay una solución mucho más simple para el problema:

Si no desea que myVariable sea definitivo, debe envolverlo en un nuevo ámbito donde 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.

+0

Esto todavía funciona sin el alcance adicional. Es efectivamente lo mismo que las otras respuestas usando el final. –

+0

@ AdamMlodzinski No, efectivamente es lo mismo que su respuesta, porque introduce una nueva variable con el valor de la variable original en un ámbito privado. – ceving

+0

No es efectivamente lo mismo. En su caso, su clase interna no puede hacer cambios en anonVar, por lo tanto, el efecto es diferente. Si su clase interna tiene, por ejemplo, mantener algún estado, su código tendría que usar algún tipo de objeto con un setter en lugar de un primitivo. –

1

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

De la misma manera, si quiere obtener el valor de una variable externa, puede crear un método que devuelva lo que desea.

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 

    }); 

} 
-2

pensé clases anónimas eran básicamente como lambdas pero con peor sintaxis ... esto resulta ser cierto, pero la sintaxis es aún peor y causas (lo que debería ser) las variables locales, que se desangra en el que contiene clase.

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

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 ordenados esto en java 8 (estoy atascado en el mundo EE y no tiene 8 todavía) 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.

+0

parece que otro truco que puede utilizar es tener una matriz de un elemento como se menciona aquí http://stackoverflow.com/a/4732586/962696 –

11

Esto va a hacer que 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)); 
1

Puede utilizar plain lambdas ("expresiones lambda puede capturar las variables")

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

o incluso una función

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

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

Uso de la función es una excelente forma de refactorizar decoradores y adaptadores, see here

Acabo de empezar a aprender sobre lambdas, por lo que si detecta un error, no dude en escribir un comentario.

Cuestiones relacionadas