method inner classes java java-8 inner-classes shadowing

inner - Inicialización y colisión del nombre del bracket doble 8 de Java



nested classes java (2)

Los ámbitos de los miembros de tipo y el sombreado es un lugar difícil para un compilador. Hubo / hay una cantidad de errores relacionados con esto, principalmente sobre tipos anidados / internos / anónimos. No puedo encontrar el que trata exactamente sobre ese tema, pero sé algunos que pueden estar relacionados con él. Aquí está el que tiene un caso muy similar a este (tipo de variable en lugar de tipo de cerramiento).

Con respecto a lo que dice la especificación sobre el sombreado, también hay un problema . Tiene referencias a JLS y describe lo que no es ideal.

La siguiente clase tiene una clase interna llamada Entry . Este código no se compilará en Java 8 ya que el compilador asume que la Entry referencia en el inicializador de Map.Entry dobles es de tipo Map.Entry y no Scope.Entry . Este código se compila en versiones anteriores (al menos 6 y 7) del JDK, pero está roto en JDK 8. Mi pregunta es "¿por qué?" Map.Entry no se importa en esta clase, por lo que no hay ninguna razón para que el compilador suponga que el valor es de tipo Map.Entry . ¿Hay algún alcance implícito que se presente o algo así para las clases anónimas?

Error:

scope/Scope.java:23: error: incompatible types: scope.Scope.Entry cannot be converted to java.util.Map.Entry for (final Entry entry : entries) { scope/Scope.java:22: error: cannot find symbol put(entry.getName(), entry);

Código de ejemplo:

package scope; import java.util.HashMap; import java.util.HashSet; import java.util.Set; public class Scope { public static class Entry<T> { public String getName() { return "Scope"; } } public static void main(String[] args) { final Set<Entry> entries = new HashSet<>(); new HashMap<String, Entry>() {{ // Why does the Java 8 compiler assume this is a Map.Entry // as it is not imported? for (final Entry entry : entries) { put(entry.getName(), entry); } }}; } }


Definitivamente no es un error, es un efecto secundario de usar la inicialización de doble llave.

new HashMap<String, Entry>() {{ for (final Entry entry : entries) { put(entry.getName(), entry); } }};

Este tipo de inicialización es básicamente una forma inteligente de abusar de bloques de inicialización de instancias . Crea una subclase anónima de HashMap con un bloque de inicialización, y luego copia ese bloque al principio de su constructor predeterminado antes de llamarlo. Esta subclase da prioridad a la Entrada en el ámbito de su principal, en lugar del ámbito en el que está anidada. Esto se explica por el sombreado .

De 8.1.6. Cuerpo de clase y declaraciones de miembros

Si C en sí es una clase anidada, puede haber definiciones del mismo tipo (variable, método o tipo) y el nombre como m en ámbitos circundantes. (Los ámbitos pueden ser bloques, clases o paquetes). En todos estos casos, el miembro m declarado en o heredado por sombras C (§6.4.1) las otras definiciones del mismo tipo y nombre. [énfasis mío]

Aquí, C es la clase interna anónima declarada. Dado que hereda de HashMap , java.util.Map.Entry sombrea scope.Scope.Entry .

En cuanto a por qué compiló como lo quería con versiones anteriores, no tengo ni idea. Este comportamiento estaba presente en esas versiones (los documentos a los que hice referencia son de 7 ), por lo que no debería haber funcionado. Entonces tal vez esas versiones tienen errores.