¿Se aplica el polimorfismo en los atributos de clase en Java?
polymorphism (8)
Sé que el uso común del polimorfismo en OOP ocurre cuando se usa una referencia de clase primaria para referirse a un objeto de clase secundaria como este:
Animal animal = new Animal();
Animal dog = new Dog();
Y sé que el polimorfismo se aplica a los métodos de clase, pero ¿también se aplica al atributo de clase? Traté de probar eso con este pequeño ejemplo:
public class Main{
public static void main(String args[]){
Animal animal = new Animal();
Animal dog1 = new Dog();
Dog dog2 = new Dog();
System.out.println("Animal object name: " + animal.name);
System.out.println("Dog1 object name: "+dog1.name);
System.out.println("Dog2 object name: " + dog2.name);
animal.print();
dog1.print();
dog2.print();
}
}
class Animal{
String name = "Animal";
public void print(){
System.out.println("I am an: "+name);
}
}
class Dog extends Animal{
String name = "Dog";
public void print(){
System.out.println("I am a: "+name);
}
}
Y esta es la salida:
Animal object name: Animal
Dog1 object name: Animal
Dog2 object name: Dog
I am an: Animal
I am a: Dog
I am a: Dog
Como puede ver (espero que esté claro), el polimorfismo funciona bien con el método print () , pero con el atributo de clase "nombre", depende de la variable de referencia.
Entonces, ¿tengo razón? El polimorfismo no se aplica a los atributos de clase?
Básicamente, cuando una clase padre tiene un hijo, la clase hija debería parecerse totalmente a su padre, de lo contrario "¿Cómo se les puede llamar padre e hijo?" ¿derecho? De todos modos, se permite que la clase secundaria tenga un comportamiento diferente al de su padre. Eso tiene mucho sentido y es natural.
Pero si desea anular el atributo de una clase secundaria, puede hacerlo a través del mecanismo constructor
Ejemplo de código
class Animal{
String name;
public Animal(){
name = "Animal";
}
public Animal(String name){
this.name = name;
}
public void print(){
System.out.println("I am an: "+name);
}
}
class Dog extends Animal{
Dog(){
super("Dog");
}
public void print(){
System.out.println("I am a: "+name);
}
}
Verá que el nombre de atributo
"Dog"
en la clase
Dog
se pasa a través del constructor, que aquí podemos llamar constructor de la clase padre a través de una palabra clave
super
.
Resultados:
Animal object name: Animal
Dog1 object name: Dog
Dog2 object name: Dog
I am an: Animal
I am a: Dog
I am a: Dog
Cuando extiende una clase, los métodos se anulan, pero los campos están ocultos. El despacho dinámico funciona para métodos, pero no para campos. ¿Por qué el lenguaje está diseñado así? Dios sabe por qué.
Cuando invocas el método de
print
en
animal
, JVM busca primero un método de
print
en un objeto
dog
.
Si no hubiera
print
método de
print
en el objeto
dog
, JVM buscaría la superclase de
Dog
.
Como encuentra el método de
print
en la clase
Dog
, comienza a ejecutarlo.
El campo de nombre en la clase
Dog
oculta el campo de nombre que se heredó de la clase
Animal
.
Es como:
public class Test {
static String name = "xyz";
public static void main(String[] args) {
{
String name = "abc";
System.out.println(name); // abc is printed
}
System.out.println(name); // xyz is printed
}
}
Dentro del bloque, hay un
name
variable local.
Entonces el
name
variable global está oculto.
Pero cuando sales del bloque, la variable local entra en vigencia.
NOTA:
Dog
clase de
Dog
debería ser como:
class Dog extends Animal{
this.name = "Dog";
public void print(){
System.out.println("I am a: " + this.name);
}
}
Lo que hiciste es un mal diseño.
El campo de Animal está oculto por el campo de Perro, aún puede acceder al campo de Animal haciendo referencia a él como lo hizo.
El comportamiento que espera puede lograrse de esta manera:
public class Main{
public static void main(String args[]){
Animal animal = new Animal();
Animal dog1 = new Dog();
Dog dog2 = new Dog();
System.out.println("Animal object name: " + animal.name);
System.out.println("Dog1 object name: "+dog1.name);
System.out.println("Dog2 object name: " + dog2.name);
animal.print();
dog1.print();
dog2.print();
}
}
class Animal {
String name = "Animal";
public void print(){
System.out.println("I am an: "+name);
}
}
class Dog extends Animal{
public Dog() {
this.name = "Dog"
}
}
En Java utilizamos el método set y get para acceder a un campo. En su ejemplo, tenemos una clase Perro que extiende una clase Animal.
Pero si lo declaras como un Animal, si llamas directamente al campo
Amimal dog1 = new Dog();
crea una instancia de Dog, pero se declara como Animal, por lo que cuando llama a
dog1.name
, le da el valor de Animal.
Las variables no son polimórficas en Java; No se anulan unos a otros.
Editar:
para respaldar aún más
la
respuesta
de Solver
, recuerdo que mi maestro de OOP afirma que cuando creas un
objeto
de
Child class
con una
reference
de la
Parent class
, las
variables
en
Child class
que no existen en la
Parent class
todavía tienen memoria asignada en el tiempo de ejecución pero no pueden se accede ya que no hay métodos en la
Parent class
que puedan acceder a una
variable
de la
Child class
.
No lo hace Las variables de instancia son propiedades de una clase específica y no se ven afectadas directamente por las superclases o subclases y el polimorfismo.
Aún puede acceder a ambos campos usando "super.name" y "this.name" en Dog, pero si usa solo "name", el de Dog se hará cargo. Si quieres el otro, explícitamente debes llamar a super. Tenga en cuenta que estoy hablando de acceder a las variables en la clase Dog .
Dog.name
está
ocultando
Animal.name
, y es un patrón
muy
malo hacerlo.
Cualquier buen IDE te advertirá que lo estás haciendo.
Ambos campos de instancia existen, y puede acceder a ambos desde
Dog
como
this.name
y
super.name
.