que example java inheritance private-methods

example - Anulación de métodos privados en Java



java reflection que es (10)

"¿Otros idiomas (C ++ o C #) tienen reglas diferentes sobre esto?"

Bueno, C ++ tiene reglas diferentes: el proceso de vinculación de funciones miembro estático o dinámico y las ejecuciones de privilegios de acceso son ortogonales.

Darle a un miembro la función de modificador de privilegios de acceso private significa que esta función solo puede ser llamada por su clase declarante, no por otros (ni siquiera las clases derivadas). Cuando declara que una función de miembro private es virtual , incluso virtual pura ( virtual void foo() = 0; ), permite que la clase base se beneficie de la especialización mientras sigue aplicando los privilegios de acceso.

Cuando se trata de funciones de miembros virtual , los privilegios de acceso le dicen lo que se supone que debe hacer:

  • private virtual significa que se le permite especializar el comportamiento, pero la invocación de la función miembro se realiza por la clase base, seguramente de forma controlada
  • protected virtual significa que debe / debe invocar la versión de clase superior de la función miembro al anularla

Entonces, en C ++, el privilegio de acceso y la virtualidad son independientes entre sí. Determinar si la función debe vincularse estática o dinámicamente es el último paso para resolver una llamada a función.

Finalmente, el patrón de diseño del Método de Plantilla debe ser preferido sobre public virtual funciones de miembro public virtual .

Referencia: Conversaciones: Virtualmente tuyo

El artículo ofrece un uso práctico de una función de miembro private virtual .

ISO / IEC 14882-2003 §3.4.1

La búsqueda de nombres puede asociar más de una declaración con un nombre si encuentra que el nombre es un nombre de función; se dice que las declaraciones forman un conjunto de funciones sobrecargadas (13.1). La resolución de sobrecarga (13.3) tiene lugar después de que la búsqueda de nombres ha tenido éxito. Las reglas de acceso (cláusula 11) se consideran solo una vez que la búsqueda de nombre y la resolución de sobrecarga de la función (si corresponde) han tenido éxito. Solo después de la búsqueda de nombres, la resolución de sobrecarga de funciones (si corresponde) y la verificación de acceso han sido exitosas son los atributos introducidos por la declaración del nombre utilizados más adelante en el procesamiento de expresiones (cláusula 5).

ISO / IEC 14882-2003 §5.2.2

La función llamada en una llamada a función miembro normalmente se selecciona de acuerdo con el tipo estático de la expresión del objeto (cláusula 10), pero si esa función es virtual y no se especifica usando aqualified-id, entonces la función realmente llamada será la desbordante final (10.3) de la función seleccionada en el tipo dinámico de la expresión del objeto [Nota: el tipo dinámico es el tipo del objeto señalado o referido por el valor actual de la expresión del objeto.

Como se describe sucintamente here , la anulación de los métodos privados en Java no es válida porque los métodos privados de una clase principal son "automáticamente finalizados y ocultos de la clase derivada". Mi pregunta es en gran parte académica.

¿Cómo no es una violación de la encapsulación no permitir que el método privado de un padre sea "anulado" (es decir, implementado independientemente, con la misma firma, en una clase infantil)? El método privado de un padre no puede ser accedido o heredado por una clase secundaria, de acuerdo con los principios de encapsulación. Está escondido

Entonces, ¿por qué se debería restringir a la clase infantil la implementación de su propio método con el mismo nombre / firma? ¿Hay una buena base teórica para esto, o es solo una solución pragmática de algún tipo? ¿Los otros idiomas (C ++ o C #) tienen reglas diferentes sobre esto?


El método privado de un padre no puede ser accedido o heredado por una clase secundaria, en línea con los principios de encapsulación. Está escondido

Entonces, ¿por qué se debería restringir a la clase infantil la implementación de su propio método con el mismo nombre / firma?

No hay tal restricción. Puedes hacerlo sin ningún problema, simplemente no se llama "anulación".

Los métodos reemplazados están sujetos a despacho dinámico, es decir, el método que realmente se llama se selecciona en tiempo de ejecución dependiendo del tipo real del objeto al que se llama. Con método privado, eso no sucede (y no debería, según su primera declaración). Y eso es lo que significa la frase "los métodos privados no pueden ser anulados".


Bien, permitir que los métodos privados se sobrescriban provocará una fuga de encapsulación o un riesgo de seguridad. Si suponemos que fue posible , obtendríamos la siguiente situación:

  1. Digamos que hay un método privado boolean hasCredentials() entonces una clase extendida podría simplemente anularla así:

    boolean hasCredentials() { return true; }

    rompiendo así el control de seguridad.

  2. La única forma de que la clase original evite esto sería declarar que su método es final . Pero ahora, esto filtra la información de implementación a través de la encapsulación, porque una clase derivada ahora no puede crear un método hasCredentials , ya que chocaría con el definido en la clase base.

    Eso es malo: digamos que este método no existe al principio en Base . Ahora, un implementador puede derivar legítimamente una clase Derived y darle un método hasCredentials que funcione como se espera.

    Pero ahora, se lanza una nueva versión de la clase Base original. Su interfaz pública no cambia (ni tampoco sus invariantes), por lo que debemos esperar que no rompa el código existente. Solo que sí, porque ahora hay un nombre que choca con un método en una clase derivada.

Creo que la pregunta surge de un malentendido:

¿Cómo es / no / una violación de la encapsulación no permitir que el método privado de un padre sea "anulado" (es decir, implementado independientemente, con la misma firma, en una clase infantil)?

El texto entre paréntesis es el opuesto al texto anterior. Java le permite "implementar de forma independiente [un método privado], con la misma firma, en una clase secundaria". No permitir esto violaría la encapsulación, como expliqué anteriormente.

Pero "no permitir que el método privado de un padre sea" anulado "" es algo diferente y necesario para asegurar la encapsulación.


Creo que estás malinterpretando lo que dice esa publicación. No es decir que la clase infantil tiene "restricciones para implementar su propio método con el mismo nombre / firma".

Aquí está el código, ligeramente editado:

public class PrivateOverride { private static Test monitor = new Test(); private void f() { System.out.println("private f()"); } public static void main(String[] args) { PrivateOverride po = new Derived(); po.f(); }); } } class Derived extends PrivateOverride { public void f() { System.out.println("public f()"); } }

Y la cita:

Es razonable esperar que la salida sea "public f ()",

El motivo de esa cita es que la variable po realidad contiene una instancia de Derivado. Sin embargo, dado que el método se define como privado, el compilador realmente mira el tipo de la variable, en lugar del tipo del objeto. Y traduce la llamada al método en invokespecial (creo que es el código de operación correcto, no se ha verificado la especificación de JVM) en lugar de invocar la instancia .


Cuando el método es privado, no es visible para su hijo. Entonces no hay ningún sentido de anularlo.


Más allá de todo lo dicho anteriormente, hay una razón muy semántica para no permitir que los métodos privados sean anulados ... ¡SON PRIVADOS!

Si escribo una clase, e indico que un método es ''privado'', el mundo exterior no debería verlo. Nadie debería poder acceder, anularlo o cualquier otra cosa. Simplemente debería poder saber que es MI método exclusivamente y que nadie más va a ensuciarlo o depender de él. No se podría considerar privado si alguien pudiera ensuciarlo. Creo que es así de simple.


Me disculpo por usar el término anular de forma incorrecta e inconsistente con mi descripción. Mi descripción describe el escenario. El siguiente código amplía el ejemplo de Jon Skeet para representar mi escenario:

class Base { public void callFoo() { foo(); } private void foo() { } } class Child extends Base { private void foo() { } }

El uso es como el siguiente:

Child c = new Child(); c.callFoo();

El problema que experimenté es que se llamaba al método padre foo () aunque, como muestra el código, estaba llamando a callFoo () en la variable de instancia hijo. Pensé que estaba definiendo un nuevo método privado foo () en Child () que llamaría el método callFoo () heredado, pero creo que algo de lo que kdgregory ha dicho puede aplicarse a mi escenario, posiblemente debido a la forma en que el constructor de clase derivado está llamando super (), o quizás no.

No había una advertencia de compilación en Eclipse y el código compilaba. El resultado fue inesperado.


No puede anular un método privado, pero puede introducir uno en una clase derivada sin ningún problema. Esto compila bien:

class Base { private void foo() { } } class Child extends Base { private void foo() { } }

Tenga en cuenta que si intenta aplicar la anotación @Override a Child.foo() obtendrá un error en tiempo de compilación. Siempre que tenga su compilador / IDE configurado para darle advertencias o errores si le falta una anotación @Override , todo debería estar bien. Es cierto que prefiero que el enfoque C # de override sea ​​una palabra clave, pero obviamente era demasiado tarde para hacerlo en Java.

En cuanto al manejo de C ''de "anular" un método privado, un método privado no puede ser virtual en primer lugar, pero ciertamente puede introducir un nuevo método privado con el mismo nombre que un método privado en la clase base.


Parece ser una cuestión de elección y definición. La razón por la que no puedes hacer esto en Java es porque la especificación lo dice, pero la pregunta era más por qué la especificación lo dice.

El hecho de que C ++ lo permita (incluso si usamos palabras clave virtuales para forzar el despacho dinámico) muestra que no hay una razón inherente por la que no pueda permitir esto.

Sin embargo, parece ser perfectamente legal reemplazar el método:

class B { private int foo() { return 42; } public int bar() { return foo(); } } class D extends B { private int foo() { return 43; } public int frob() { return foo(); } }

Parece compilar OK (en mi compilador), pero el D.foo no está relacionado con B.foo (es decir, no lo anula) - bar () siempre devuelve 42 (llamando a B.foo) y frob () siempre devuelve 43 (llamando a D.foo) sin importar si se invocó en una instancia B o D.

Una de las razones por las que Java no permite anular el método sería que no les gustaría permitir que se cambie el método como en el ejemplo de Konrad Rudolph. Tenga en cuenta que C ++ difiere aquí ya que necesita utilizar la palabra clave "virtual" para obtener el despacho dinámico; de manera predeterminada no lo tiene, por lo que no puede modificar el código en la clase base que se basa en el método hasCredentials. El ejemplo anterior también protege contra esto ya que D.foo no reemplaza las llamadas a foo desde B.


Una clase se define por qué métodos pone a disposición y cómo se comportan. No cómo se implementan internamente (por ejemplo, a través de llamadas a métodos privados).

Como la encapsulación tiene que ver con el comportamiento y no con los detalles de implementación, los métodos privados no tienen nada que ver con la encapsulación de la idea. En cierto sentido, tu pregunta no tiene sentido. Es como preguntar "¿Cómo poner la crema en el café no es una violación de la encapsulación?"

Es de suponer que el método privado es utilizado por algo que es público. Puedes anular eso. Al hacerlo, has cambiado el comportamiento.