¿Es posible hacer clases internas anónimas en Java estático?

123

En Java, las clases anidadas pueden ser statico no. Si lo son static, no contienen una referencia al puntero de la instancia que lo contiene (ya no se llaman clases internas, se llaman clases anidadas).

Olvidar hacer una clase anidada staticcuando no necesita esa referencia puede ocasionar problemas con la recolección de basura o el análisis de escape.

¿Es posible hacer una clase interna anónima statictambién? ¿O el compilador resuelve esto automáticamente (que podría, porque no puede haber ninguna subclase)?

Por ejemplo, si hago un comparador anónimo, casi nunca necesito la referencia al exterior:

  Collections.sort(list, new Comparator<String>(){
       int compare(String a, String b){
          return a.toUpperCase().compareTo(b.toUpperCase());
       }
  }
Thilo
fuente
¿Cuáles son los problemas con la "recolección de basura o el análisis de escape" cuando se olvida de hacer una clase interna estática? Pensé que esto se trata de rendimiento sólo ...
Tim Büthe
17
Su instancia de clase interna mantiene viva una referencia a su instancia externa, incluso si no la necesita. Esto podría evitar que las cosas se recojan. Imagine un objeto de fábrica (con muchos recursos) que crea instancias ligeras de algo. Después de que la fábrica haya hecho su trabajo (por ejemplo, durante el inicio de la aplicación), podría eliminarse, pero eso solo funciona si las cosas que ha creado no se vinculan.
Thilo el
Lo sé, este es solo un ejemplo, pero dado que es recurrente, debería mencionarse que Collections.sort(list, String.CASE_INSENSITIVE_ORDER)funciona desde Java 2, lea, ya que existe la API de colección ...
Holger

Respuestas:

138

No, no puedes, y no, el compilador no puede resolverlo. Es por eso que FindBugs siempre sugiere cambiar las clases internas anónimas a clases staticanidadas con nombre si no usan su thisreferencia implícita .

Editar: Tom Hawtin - tackline dice que si la clase anónima se crea en un contexto estático (por ejemplo, en el mainmétodo), la clase anónima sí lo es static. Pero el JLS no está de acuerdo :

Una clase anónima nunca es abstract(§8.1.1.1). Una clase anónima es siempre una clase interna (§8.1.3); nunca lo es static(§8.1.1, §8.5.1). Una clase anónima siempre está implícita final(§8.1.1.2).

El Glosario de Java de Roedy Green dice que el hecho de que se permitan clases anónimas en un contexto estático depende de la implementación:

Si desea confundir a aquellos que mantienen su código, los wags descubiertos javac.exepermitirán clases anónimas dentro del staticcódigo y los staticmétodos de inicio , aunque la especificación del lenguaje dice que las clases anónimas nunca lo son static. Estas clases anónimas, por supuesto, no tienen acceso a los campos de instancia del objeto. No recomiendo hacer esto. La característica se puede extraer en cualquier momento.

Edición 2: El JLS en realidad cubre contextos estáticos más explícitamente en §15.9.2 :

Deje que C sea ​​la clase que se instancia, y que yo sea ​​la instancia que se está creando. Si C es una clase interna entonces i puede tener un ejemplo inmediatamente circundante. La instancia de cierre inmediato de i (§8.1.3) se determina como sigue.

  • Si C es una clase anónima, entonces:
    • Si la expresión de creación de la instancia de clase se produce en un contexto estático (§8.1.3), entonces i ha encierra no inmediatamente ejemplo.
    • De lo contrario, la instancia de cierre inmediato de i es this.

Entonces, una clase anónima en un contexto estático es más o menos equivalente a una staticclase anidada en el sentido de que no mantiene una referencia a la clase adjunta, aunque técnicamente no es una staticclase.

Michael Myers
fuente
19
+1 para FindBugs: todos los desarrolladores de Java deberían tener esto en su compilación.
Andrew Duffy
13
Eso es muy desafortunado, porque significa que es posible que desee evitar esta sintaxis casi concisa por razones de rendimiento.
Thilo el
2
JLS 3rd Ed trata el caso de las clases internas en contextos estáticos. No son estáticos en el sentido JLS, pero son estáticos en el sentido dado en la pregunta.
Tom Hawtin - tackline
66
Aquí hay un ejemplo de cómo depende de la implementación: este código se imprime trueusando javac (sun-jdk-1.7.0_10) y falseusando el compilador Eclipse.
Paul Bellora
1
@MichaelMyers He intentado simular los FindBugs alertándome de hacer un Anonymous Inner sin usar la referencia 'this', y no pasa nada. ¿Puedes demostrar cómo FindBugs te alerta como dijiste al comienzo de tu respuesta? Solo pegue algún enlace o lo que sea.
Thufir Hawat
15

Mas o menos. Una clase interna anónima creada en un método estático obviamente será efectivamente estática porque no hay una fuente para esto externo.

Existen algunas diferencias técnicas entre las clases internas en contextos estáticos y las clases anidadas estáticas. Si estás interesado, lee el JLS 3rd Ed.

Tom Hawtin - tackline
fuente
En realidad, lo retiro; JLS no está de acuerdo. java.sun.com/docs/books/jls/third%5Fedition/html/… : "Una clase anónima siempre es una clase interna; nunca es estática".
Michael Myers
1
estático en un sentido diferente al de la pregunta.
Tom Hawtin - tackline
1
He agregado una pequeña aclaración.
Tom Hawtin - tackline
15

Creo que hay un poco de confusión en la nomenclatura aquí, que sin duda es demasiado tonta y confusa.

Como sea que los llame, estos patrones (y algunas variaciones con diferente visibilidad) son todos Java posibles, normales y legales:

public class MyClass {
  class MyClassInside {
  }
}

public class MyClass {
  public static class MyClassInside {
  }
}

public class MyClass {
  public void method() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

public class MyClass {
  public static void myStaticMethod() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

Están atendidos en la especificación del idioma (si está realmente molesto, consulte la sección 15.9.5.1 para conocer el que está dentro del método estático).

Pero esta cita es simplemente errónea :

javac.exe permitirá clases anónimas dentro del código de inicio estático y métodos estáticos, aunque la especificación del lenguaje dice que las clases anónimas nunca son estáticas

Creo que el autor citado confunde la palabra clave estática con el contexto estático . (Es cierto que el JLS también es un poco confuso a este respecto).

Honestamente, todos los patrones anteriores están bien (como los llames "anidados", "internos", "anónimos", lo que sea ...). Realmente, nadie va a eliminar repentinamente esta funcionalidad en la próxima versión de Java. ¡Honestamente!

Neil Coffey
fuente
2
"(Es cierto que el JLS también es un poco confuso a este respecto.)" Lo entendiste bien. Parecía extraño decir que depende de la implementación, pero no recuerdo haber visto ningún error obvio en el Glosario de Java antes. De ahora en adelante, lo tomo con un grano de sal.
Michael Myers
2
En realidad no estamos hablando de ninguno de los patrones. Queremos decir que la clase anónima anidada es estática. Es decir, agregue un "estático" entre newy JComponenten su tercer ejemplo.
Timmmm
Agregué una aclaración a la pregunta original para mostrar lo que se desea.
Timmmm
@MichaelMyers, El dictado en JLS siempre necesita ser interpretado.
Pacerier
6

Las clases internas no pueden ser estáticas: una clase anidada estática no es una clase interna. El tutorial de Java habla sobre esto aquí .

Andrew Duffy
fuente
1
He actualizado la pregunta con una referencia a la nomenclatura oficial.
Thilo el
0

las clases internas anónimas nunca son estáticas (no pueden declarar métodos estáticos o campos estáticos no finales), pero si se definen en un contexto estático (método estático o campo estático) se comportan como estáticos en el sentido de que no pueden Acceder a miembros no estáticos (es decir, instancia) de la clase que los encierra (como todo lo demás desde un contexto estático)

Luca
fuente
-3

En la nota de hacer una clase interna anónima estática llamándolas dentro de un método estático.

Esto en realidad no elimina la referencia. Puede probar esto intentando serializar la clase anónima y no haciendo serializable la clase adjunta.

Terra Caines
fuente
55
-1: Creación de una clase anónima dentro de un método estático en realidad no eliminar la referencia a la clase externa. Puede probar esto intentando serializar la clase anónima y no haciendo serializable la clase adjunta. (Acabo de hacerlo.)
Christian Semrau