¿Cómo se usan las clases internas anónimas en Java?


Respuestas:


369

Por "clase anónima", supongo que te refieres a la clase interna anónima .

Una clase interna anónima puede ser útil al hacer una instancia de un objeto con ciertos "extras", como anular métodos, sin tener que subclasificar una clase.

Tiendo a usarlo como atajo para adjuntar un detector de eventos:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do something
    }
});

El uso de este método hace que la codificación sea un poco más rápida, ya que no necesito hacer una clase adicional que se implemente ActionListener, solo puedo crear una instancia de una clase interna anónima sin hacer una clase separada.

Solo uso esta técnica para tareas "rápidas y sucias" en las que hacer que una clase entera se sienta innecesaria. Tener múltiples clases internas anónimas que hacen exactamente lo mismo debe refactorizarse a una clase real, ya sea una clase interna o una clase separada.


55
O podría refactorizar clases internas anónimas duplicadas en un método con la clase interna anónima (y posiblemente algún otro código duplicado).
Tom Hawtin - tackline

3
Gran respuesta pero una pregunta rápida. ¿Significa que Java puede vivir sin clases internas anónimas y que son una opción adicional para elegir?
realPK

55
Muy bien explicado, incluso difícil. Sugeriría a cualquiera que lea esto que mire hacia arriba y vea qué pueden hacer las expresiones java 8 y lambda para hacer que la codificación sea más rápida y legible.
Pievis

2
@ user2190639 Precisamente, no puedo pedir nada mejor con Lambda en Java8
bonCodigo

3
¿Por qué dijiste overloading methodsy no overriding methods?
Tarun

73

Las clases internas anónimas son cierres efectivos, por lo que pueden usarse para emular expresiones lambda o "delegados". Por ejemplo, tome esta interfaz:

public interface F<A, B> {
   B f(A a);
}

Puede usar esto de forma anónima para crear una función de primera clase en Java. Digamos que tiene el siguiente método que devuelve el primer número más grande que i en la lista dada, o i si ningún número es mayor:

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return i;
}

Y luego tiene otro método que devuelve el primer número más pequeño que i en la lista dada, o i si ningún número es más pequeño:

public static int smaller(final List<Integer> ns, final int i) {
   for (Integer n : ns)
      if (n < i)
         return n;
   return i;
}

Estos métodos son casi idénticos. Usando la función de primera clase tipo F, podemos reescribirlas en un método de la siguiente manera:

public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
   for (T t : ts)
      if (f.f(t))
         return t;
   return z;
}

Puede usar una clase anónima para usar el método firstMatch:

F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
   Boolean f(final Integer n) {
      return n > 10;
   }
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);

Este es un ejemplo realmente artificial, pero es fácil ver que poder pasar funciones como si fueran valores es una característica bastante útil. Consulte "¿Puede su lenguaje de programación hacer esto" por el propio Joel.

Una buena biblioteca para programar Java en este estilo: Java funcional.


20
desafortunadamente, la verbosidad de hacer programación funcional en Java, en mi humilde opinión, supera sus ganancias: uno de los puntos sorprendentes de la programación funcional es que tiende a reducir el tamaño del código y hace que las cosas sean más fáciles de leer y modificar. Pero Java funcional no parece hacer eso en absoluto.
Chii

27
¡Toda la comprensión de la programación funcional, con la brevedad de Java!
Adam Jaskiewicz

3
En mi experiencia, el estilo funcional en Java se paga con verbosidad por adelantado, pero produce brevedad a largo plazo. Por ejemplo, myList.map (f) es considerablemente menos detallado que el bucle for correspondiente.
Apocalisp

2
Scala , un lenguaje de estilo de programación funcional, supuestamente funciona bien dentro de la JVM y puede ser una opción para escenarios de programación funcional.
Darrell Teague el

51

La clase interna anónima se utiliza en el siguiente escenario:

1.) Para la anulación (subclasificación), cuando la definición de clase no se puede utilizar, excepto el caso actual:

class A{
   public void methodA() {
      System.out.println("methodA");
    }
}
class B{
    A a = new A() {
     public void methodA() {
        System.out.println("anonymous methodA");
     }
   };
}

2.) Para implementar una interfaz, cuando la implementación de la interfaz se requiere solo para el caso actual:

interface interfaceA{
   public void methodA();
}
class B{
   interfaceA a = new interfaceA() {
     public void methodA() {
        System.out.println("anonymous methodA implementer");
     }
   };
}

3.) Argumento definido clase interna anónima:

 interface Foo {
   void methodFoo();
 }
 class B{
  void do(Foo f) { }
}

class A{
   void methodA() {
     B b = new B();
     b.do(new Foo() {
       public void methodFoo() {
         System.out.println("methodFoo");
       } 
     });
   } 
 } 

8
Gran respuesta, parece 3.) es el patrón utilizado para los oyentes de eventos
xdl

47

A veces los uso como un truco de sintaxis para la creación de instancias de mapas:

Map map = new HashMap() {{
   put("key", "value");
}};

vs

Map map = new HashMap();
map.put("key", "value");

Ahorra algo de redundancia al hacer muchas declaraciones put. Sin embargo, también he tenido problemas para hacer esto cuando la clase externa necesita ser serializada a través de comunicación remota.


56
Para ser claros, el primer conjunto de llaves es la clase interna anónima (subclase HashMap). El segundo conjunto de llaves es un inicializador de instancia (en lugar de uno estático) que luego establece los valores en su subclase de HashMap. +1 por mencionarlo, -1 por no explicarlo a los novatos. ;-D
Spencer Kormos

44
Lea más sobre la sintaxis de doble paréntesis aquí .
Martin Andersson


18

Se usan comúnmente como una forma detallada de devolución de llamada.

Supongo que se podría decir que son una ventaja en comparación con no tenerlos y tener que crear una clase con nombre cada vez, pero conceptos similares se implementan mucho mejor en otros idiomas (como cierres o bloques)

Aquí hay un ejemplo de swing

myButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        // do stuff here...
    }
});

Aunque todavía es desordenadamente detallado, es mucho mejor que forzarlo a definir una clase con nombre para cada oyente desechado como este (aunque dependiendo de la situación y la reutilización, ese puede ser el mejor enfoque)


1
¿Querías decir conciso? Si fuera detallado, la devolución de llamada se mantendría por separado, lo que lo haría un poco más grande y, por lo tanto, lo haría detallado. Si dices que esto todavía es detallado, ¿cuál sería una forma concisa entonces?
user3081519

1
@ user3081519, algo como myButton.addActionListener(e -> { /* do stuff here */})o myButton.addActionListener(stuff)sería terser.
Samuel Edwin Ward

8

Lo usa en situaciones en las que necesita crear una clase para un propósito específico dentro de otra función, por ejemplo, como oyente, como ejecutable (para generar un hilo), etc.

La idea es que los llame desde dentro del código de una función para que nunca se refiera a ellos en otro lugar, por lo que no necesita nombrarlos. El compilador solo los enumera.

Son esencialmente azúcares sintácticos, y generalmente deben trasladarse a otro lugar a medida que crecen.

No estoy seguro de si es una de las ventajas de Java, aunque si los usa (y todos los usamos con frecuencia, desafortunadamente), podría argumentar que son uno.


6

Lineamientos para la clase anónima.

  1. La clase anónima se declara e inicializa simultáneamente.

  2. La clase anónima debe extenderse o implementarse a una y solo una clase o interfaz resp.

  3. Como la clase anónima no tiene nombre, solo se puede usar una vez.

p.ej:

button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub

    }
});

Con respecto al n. ° 3: no del todo cierto. Puede adquirir múltiples instancias de una clase anónima con reflexión, por ejemplo ref.getClass().newInstance().
icza

Las reglas no responden la pregunta.
Marqués de Lorne

5

Sí, las clases internas anónimas son definitivamente una de las ventajas de Java.

Con una clase interna anónima, tiene acceso a las variables finales y miembros de la clase circundante, y eso es útil para los oyentes, etc.

Pero una ventaja importante es que el código de la clase interna, que (al menos debería estar) estrechamente acoplado a la clase / método / bloque circundante, tiene un contexto específico (la clase, el método y el bloque circundantes).


1
¡Tener acceso a la clase que lo rodea es muy importante! Creo que esta es la razón en muchos casos donde se usa una clase anónima, porque necesita / usa los atributos no públicos, métodos y variables locales de la clase / método circundante que externamente (si se usara una clase separada) tendría que ser aprobado o publicado
icza

5
new Thread() {
        public void run() {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                System.out.println("Exception message: " + e.getMessage());
                System.out.println("Exception cause: " + e.getCause());
            }
        }
    }.start();

Este es también uno de los ejemplos para el tipo interno anónimo que usa hilo


3

Yo uso objetos anónimos para llamar nuevos hilos.

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();

3

Una clase interna está asociada con una instancia de la clase externa y hay dos clases especiales: clase local y clase anónima . Una clase anónima nos permite declarar e instanciar una clase al mismo tiempo, por lo tanto, hace que el código sea conciso. Los usamos cuando necesitamos una clase local solo una vez, ya que no tienen un nombre.

Considere el ejemplo de doc donde tenemos una Personclase:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public void printPerson() {
        // ...
    }
}

y tenemos un método para imprimir miembros que coinciden con los criterios de búsqueda como:

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

donde CheckPersonhay una interfaz como:

interface CheckPerson {
    boolean test(Person p);
}

Ahora podemos hacer uso de la clase anónima que implementa esta interfaz para especificar criterios de búsqueda como:

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

Aquí la interfaz es muy simple y la sintaxis de la clase anónima parece difícil de manejar y poco clara.

Java 8 ha introducido un término interfaz funcional que es una interfaz con un solo método abstracto, por lo tanto, podemos decir que CheckPersones una interfaz funcional. Podemos hacer uso de la expresión Lambda que nos permite pasar la función como argumento de método como:

printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

Podemos usar una interfaz funcional estándar Predicateen lugar de la interfaz CheckPerson, lo que reducirá aún más la cantidad de código requerido.


2

La clase interna anónima puede ser beneficiosa al proporcionar diferentes implementaciones para diferentes objetos. Pero debe usarse con moderación, ya que crea un problema para la legibilidad del programa.


1

Uno de los principales usos de las clases anónimas en la finalización de clase que se llama guardián finalizador . En el mundo Java, debe evitarse el uso de los métodos de finalización hasta que realmente los necesite. Debe recordar que cuando anula el método de finalización para subclases, siempre debe invocar super.finalize()también, porque el método de finalización de superclase no invocará automáticamente y puede tener problemas con pérdidas de memoria.

considerando el hecho mencionado anteriormente, puede usar las clases anónimas como:

public class HeavyClass{
    private final Object finalizerGuardian = new Object() {
        @Override
        protected void finalize() throws Throwable{
            //Finalize outer HeavyClass object
        }
    };
}

Usando esta técnica, usted se alivió a sí mismo y a sus otros desarrolladores para recurrir super.finalize()a cada subclase del HeavyClassmétodo que necesita finalizar.


1

Puedes usar la clase anónima de esta manera

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});

1

Parece que nadie mencionó aquí, pero también puede usar una clase anónima para mantener el argumento de tipo genérico (que normalmente se pierde debido a la eliminación de tipo) :

public abstract class TypeHolder<T> {
    private final Type type;

    public TypeReference() {
        // you may do do additional sanity checks here
        final Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public final Type getType() {
        return this.type;
    }
}

Si crea una instancia de esta clase de forma anónima

TypeHolder<List<String>, Map<Ineger, Long>> holder = 
    new TypeHolder<List<String>, Map<Ineger, Long>>() {};

entonces dicha holderinstancia contendrá una definición no borrada del tipo pasado.

Uso

Esto es muy útil para construir validadores / deserializadores. También puede crear instancias de tipo genérico con reflexión (por lo que si alguna vez ha querido hacerlo new T()en tipo parametrizado, ¡de nada !) .

Inconvenientes / limitaciones

  1. Debe pasar el parámetro genérico explícitamente. De lo contrario, se perderá el parámetro de tipo
  2. Cada instancia le costará clase adicional que será generada por el compilador que conduce a la contaminación classpath / hinchazón

1

La mejor manera de optimizar el código. Además, podemos utilizar un método de anulación de una clase o interfaz.

import java.util.Scanner;
abstract class AnonymousInner {
    abstract void sum();
}

class AnonymousInnerMain {
    public static void main(String []k){
        Scanner sn = new Scanner(System.in);
        System.out.println("Enter two vlaues");
            int a= Integer.parseInt(sn.nextLine());
            int b= Integer.parseInt(sn.nextLine()); 
        AnonymousInner ac = new AnonymousInner(){
            void sum(){
                int c= a+b;
                System.out.println("Sum of two number is: "+c);
            }
        };
        ac.sum();
    }

}

1

Una clase interna anónima se usa para crear un objeto al que nunca se hará referencia nuevamente. No tiene nombre y se declara y crea en la misma declaración. Esto se usa donde normalmente usaría la variable de un objeto. Reemplaza la variable con la newpalabra clave, una llamada a un constructor y la definición de clase dentro {y }.

Al escribir un programa de subprocesos en Java, generalmente se vería así

ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();

El ThreadClassutilizado aquí sería definido por el usuario. Esta clase implementará la Runnableinterfaz que se requiere para crear hilos. En el ThreadClassel run()método (sólo método en Runnable) necesita ser implementado también. Está claro que deshacerse de ThreadClassél sería más eficiente y es exactamente por eso que existen las clases internas anónimas.

Mira el siguiente código

Thread runner = new Thread(new Runnable() {
    public void run() {
        //Thread does it's work here
    }
});
runner.start();

Este código reemplaza la referencia hecha tasken el ejemplo más superior. En lugar de tener una clase separada, la clase interna anónima dentro del Thread()constructor devuelve un objeto sin nombre que implementa la Runnableinterfaz y anula el run()método. El método run()incluiría declaraciones dentro que hacen el trabajo requerido por el hilo.

Respondiendo a la pregunta sobre si las clases internas anónimas son una de las ventajas de Java, tendría que decir que no estoy muy seguro ya que no estoy familiarizado con muchos lenguajes de programación en este momento. Pero lo que puedo decir es que definitivamente es un método de codificación más rápido y fácil.

Referencias: Sams Teach Yourself Java en 21 días Séptima edición


0

Una ventaja más:
como sabe que Java no admite la herencia múltiple, por lo que si usa la clase "Thread" como clase anónima, la clase todavía tiene un espacio para que cualquier otra clase se extienda.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.