tutorial paso interfaz grafica español ejemplos componentes clase java language-lawyer java-9

java - paso - Método de referencia al método de interfaz privada



java swing tutorial pdf español (4)

Este no es un problema nuevo y no tiene nada que ver con los métodos de interfaz privada o las referencias de métodos.

Si cambia el código para ampliar una clase en lugar de implementar una interfaz, y para llamar al método en lugar de hacer referencia a él, todavía tendrá el mismo problema.

class A { public static void main(String[] args) { ((I)(new I() {})).test(); // compiles OK ((new I() {})).test(); // won''t compile } class I { private void test() {} } }

Sin embargo, ese código se puede aplicar a versiones anteriores de Java, y probé con Java 9, 8, 7, 6, 5 y 1.4. Todos se comportan igual !!

El problema es que los métodos privados no se heredan 1 , por lo que la clase anónima no tiene el método, en absoluto. Como el método privado ni siquiera existe en la clase anónima, no se puede llamar.

Cuando lanzas a I , ahora existe el método para que lo vea el compilador, y como I es una clase interna, se te otorga acceso (a través de un método sintético), aunque sea privado.

En mi opinión, no es un error. Es cómo funcionan los métodos privados en el contexto de la herencia.

1) Según lo encontrado por Jorn Vernee en JLS 6.6-5 : "[Un miembro de clase privada] no es heredado por subclases" .

Considere el siguiente código:

public class A { public static void main(String[] args) { Runnable test1 = ((I)(new I() {}))::test; // compiles OK Runnable test2 = ((new I() {}))::test; // won''t compile } interface I { private void test() {} } }

Realmente no entiendo el punto ... Entiendo que el método test() es privado . Pero, ¿qué cambia si enviamos una clase anónima a su interfaz ((I)(new I() {})) ? Más precisamente, me gustaría ver un punto JLS particular que permita ese truco.

PS Lo he reportado como un error del compilador (ID: 9052217). Me parece que Runnable test2 = ((new I() {}))::test; Debe ser bien compilado en este caso particular.

PPS Hasta ahora se creó un error basado en mi informe: https://bugs.openjdk.java.net/browse/JDK-8194998 . Puede ser que se cierre como "no se arregla" o lo que sea.


Esto es contraintuitivo. Primero simplifiquemos esto un poco:

static interface Inter { private void test() { System.out.println("test"); } } public static void main(String[] args) { ((Inter) new Inter() { }).hashCode(); }

Esto tiene sentido cuando llama al método de hashCode público, aquí está el código de byte (solo parte importante) para él:

public static void main(java.lang.String[]); Code: 0: new #2 // class com/test/DeleteMe$1 3: dup 4: invokespecial #3 // Method com/test/DeleteMe$1."<init>":()V 7: invokevirtual #4 // Method java/lang/Object.hashCode:()I 10: pop 11: return

Me parece muy sano. Ahora vamos a cambiar eso para llamar a test() :

public static void main(String[] args) { ((Inter) new Inter() { }).test(); }

El código de bytes para esto:

invokestatic #4 // InterfaceMethod com/test/DeleteMe$Inter.access$000:(Lcom/test/DeleteMe$Inter;)V

Dado que los métodos privados no se heredan, en realidad se está "yendo" a ese método a través del método sintético estático access$n .


Invocar un método private solo es posible a través de una expresión exactamente del tipo declarante, independientemente del escenario.

Vamos a explicarlo con el ejemplo más simple.

public class A { public static void main(String[] args) { B b = new B(); b.someMethod(); // does not compile A a = b; a.someMethod(); // no problem } private void someMethod() {} } class B extends A { }

Puede esperar que esto se compile utilizando b.someMethod() para invocar el método b.someMethod() de A Pero que tal si B fuera declarado como

class B extends A { public void someMethod() {} }

Esto es posible, ya que el private void someMethod() no se hereda, por lo que public void someMethod() no lo anula. Pero debe quedar claro que ahora b.someMethod() debe invocar el método de B

Entonces, si se permitiera que b.someMethod() termine en un método private de A , dependería de si B declara otro someMethod() , en qué método real terminará la llamada. Y eso, obviamente, contradice todo el concepto de métodos private . private métodos private no se heredan y nunca se anulan, por lo que no debería depender de la subclase, ya sea que una llamada termine en un método private o en un método de subclase.

Tu ejemplo es similar. La clase interna anónima que implementa podría declarar su propio método test() , p. Ej. Runnable test2 = ((new I() {void test() {}}))::test; por lo que dependería de esa clase interna anónima, ya sea que se invoque el método private de I o un método de esa clase interna anónima, lo que sería inaceptable. Por supuesto, con una clase interna de este tipo, que precede directamente a la invocación o al método de referencia, un lector puede decir de inmediato en qué método terminará la invocación, pero sería muy inconsistente, si esto se permitiera para una clase interna anónima, pero nada más.

El método private de I es accesible para A ya que es una interfaz anidada, pero como se muestra con el ejemplo más simple de arriba, la regla no tiene que ver con la accesibilidad, ya que la regla se aplica incluso cuando el método private está dentro de la misma clase que el que llama. .


private métodos private no se heredan (el más cercano que encontré hasta ahora es: JLS6.6-5 : "[Un miembro de clase privada] no es heredado por subclases" ). Eso significa que no puede acceder a un método privado, desde un subtipo (porque simplemente no ''tiene'' ese método). Por ejemplo:

public static void main(String[] args) { I1 i1 = null; I2 i2 = null; i1.test(); // works i2.test(); // method test is undefined } interface I1 { private void test() {} } interface I2 extends I1 {}

Eso también significa que no puede acceder directamente al método de test través del tipo de una subclase anónima. El tipo de expresión:

(new I() {})

No es I , pero en realidad es el tipo no denotable de la subclase anónima, por lo que no puede acceder a la test través de él.

Sin embargo, el tipo de la expresión:

((I) (new I() {}))

es I (como lo ha hecho explícitamente con I ), por lo que puede acceder al método de test través de él. (al igual que puedes hacer ((I1) i2).test(); en mi ejemplo anterior)

Se aplican reglas similares a static métodos static , ya que tampoco se heredan.