studio reales proyectos programacion libro introducción incluye herramientas fundamentos fuente español código con avanzado aplicaciones java inheritance jls

reales - ¿Por qué Java enlaza variables en tiempo de compilación?



libro de android studio en español pdf (4)

El comportamiento polimórfico del lenguaje java funciona con métodos y no con variables miembro: diseñaron el lenguaje para unir variables miembro en tiempo de compilación.

Considere el siguiente código de ejemplo

class MyClass { public String var = "base"; public void printVar() { System.out.println(var); } } class MyDerivedClass extends MyClass { public String var = "derived"; public void printVar() { System.out.println(var); } } public class Binding { public static void main(String[] args) { MyClass base = new MyClass(); MyClass derived = new MyDerivedClass(); System.out.println(base.var); System.out.println(derived.var); base.printVar(); derived.printVar(); } }

da el siguiente resultado

base base base derived

Las llamadas a métodos se resuelven en tiempo de ejecución y se llama al método anulado correcto, como se esperaba.
El acceso a las variables se resuelve en tiempo de compilación como más tarde aprendí. Esperaba una salida como

base derived base derived

porque en la clase derivada la redefinición de var sombrea la de la clase base.
¿Por qué el enlace de variables ocurre en tiempo de compilación y no en tiempo de ejecución? ¿Es esto solo por razones de rendimiento?


En Java, esto es por diseño. Porque, la configuración de los campos que se resolverán dinámicamente haría que las cosas se ejecuten un poco más lentamente. Y en realidad, no hay ninguna razón para hacerlo. Desde entonces, puede hacer que sus campos en cualquier clase sean privados y acceder a ellos con métodos que se resuelven dinámicamente .

Por lo tanto, los campos se resuelven mejor en tiempo de compilación :)


La razón se explica en la Especificación del lenguaje Java en un ejemplo en la Sección 15.11 , citado a continuación:

...

La última línea muestra que, de hecho, el campo al que se accede no depende de la clase de tiempo de ejecución del objeto referenciado; incluso si s contiene una referencia a un objeto de la clase T , la expresión sx refiere al campo x de la clase S , porque el tipo de la expresión s es S Los objetos de la clase T contienen dos campos llamados x , uno para la clase T y otro para su superclase S

Esta falta de búsqueda dinámica de accesos de campo permite que los programas se ejecuten de manera eficiente con implementaciones sencillas. El poder de vinculación y anulación tardía está disponible, pero solo cuando se utilizan métodos de instancia ...

Entonces sí, el rendimiento es una razón. La especificación de cómo se evalúa la expresión de acceso de campo se establece de la siguiente manera:

  • Si el campo no es static :

    ...

    • Si el campo no es un final blanco, el resultado es el valor del campo miembro nombrado en el tipo T encuentra en el objeto al que hace referencia el valor del primario .

donde Primario en su caso se refiere a la variable derived que es de tipo MyClass .

Otra razón, como sugirió @Clashsoft, es que en las subclases, los campos no se anulan, están ocultos . Por lo tanto, tiene sentido permitir a qué campos acceder en función del tipo declarado o utilizando un reparto. Esto también es cierto para los métodos estáticos. Es por eso que el campo se determina en función del tipo declarado. A diferencia de la anulación por métodos de instancia donde depende del tipo real. La cita anterior de JLS menciona esta razón implícitamente:

El poder de vinculación y anulación tardía está disponible, pero solo cuando se utilizan métodos de instancia.


Si bien puede tener razón sobre el rendimiento, hay otra razón por la cual los campos no se envían dinámicamente: no podría acceder al campo MyClass.var si tuviera una instancia de MyDerivedClass .

En general, no conozco ningún lenguaje de tipo estático que tenga una resolución variable dinámica. Pero si realmente lo necesita, puede hacer getters o métodos de acceso (lo que debería hacerse en la mayoría de los casos para evitar campos public , de todos modos):

class MyClass { private String var = "base"; public String getVar() // or simply ''var()'' { return this.var; } } class MyDerivedClass extends MyClass { private String var = "derived"; @Override public String getVar() { return this.var; } }