propiedades programacion libro introduccion interfaz grafica ejemplos desde cero java oop inheritance casting upcasting

programacion - ¿Por qué asignamos una referencia padre al objeto hijo en Java?



textarea java netbeans (8)

Cuando compila su programa, la variable de referencia de la clase base obtiene memoria y el compilador comprueba todos los métodos de esa clase. Por lo tanto, comprueba todos los métodos de la clase base, pero no los métodos de la clase infantil. Ahora, en el tiempo de ejecución cuando se crea el objeto, solo se pueden ejecutar los métodos comprobados. En caso de que un método sea anulado en la clase de niño esa función se ejecuta. Las otras funciones de clase secundaria no se ejecutan porque el compilador no las ha reconocido en el momento de la compilación.

Estoy haciendo una pregunta bastante simple, pero estoy un poco confundido en esto.

Supongamos que tengo una clase Parent :

public class Parent { int name; }

Y tener otra clase Child.java :

public class Child extends Parent{ int salary; }

Y finalmente mi clase Main.java

public class Main { public static void main(String[] args) { Parent parent = new Child(); parent.name= "abcd"; } }

Si hago un objeto hijo como

Child child = new Child():

Entonces child objeto child puede acceder tanto al name and salary variables name and salary .

Mi pregunta es:

Parent parent = new Child();

le da acceso solo a la variable de name de la clase para padres. Entonces, ¿cuál es el uso exacto de esta línea?

Parent parent = new Child();

Y también cuando está usando polimorfismo dinámico, ¿por qué la variable de clase infantil no es accesible después de hacer esto?

Parent parent = new Child();


Es sencillo.

Parent parent = new Child();

En este caso, el tipo de objeto es Parent . Ant Parent tiene solo una propiedad. Es el name

Child child = new Child();

Y en este caso, el tipo de objeto es Child . Ant Child tiene dos propiedades. Son name y salary .

El hecho es que no es necesario inicializar el campo no final inmediatamente en la declaración. Por lo general, esto se hace en tiempo de ejecución porque a menudo no se puede saber exactamente qué implementación exactamente necesitarás. Por ejemplo, imagine que tiene una jerarquía de clases con la clase Transport a la cabeza. Y tres subclases: Car , Helicopter y Boat . Y hay otra clase Tour que tiene el campo Transport . Es decir:

class Tour { Transport transport; }

Siempre que un usuario no haya reservado un viaje y no haya elegido un tipo particular de transporte, no puede inicializar este campo. Es primero.

Segundo, suponga que todas estas clases deben tener un método go() pero con una implementación diferente. Puede definir una implementación básica por defecto en el Transport superclase y implementaciones únicas propias en cada subclase. Con esta inicialización Transport tran; tran = new Car(); Transport tran; tran = new Car(); puede llamar al método tran.go() y obtener el resultado sin preocuparse por la implementación específica. Llamará al método anulado de una subclase particular.

Además, puede usar una instancia de subclase en todas partes donde se use una instancia de superclase. Por ejemplo, desea proporcionar la oportunidad de alquilar su transporte. Si no usa polimorfismo, debe escribir muchos métodos para cada caso: rentCar(Car car) , rentBoat(Boat boat) etc. Al mismo tiempo, el polimorfismo le permite crear una rent(Transport transport) método universal rent(Transport transport) . Puede pasarlo objeto de cualquier subclase de Transport . Además, si con el tiempo su lógica aumentará y tendrá que crear otra clase en la jerarquía? Cuando usa polimorfismo, no necesita cambiar nada. Simplemente extienda la clase Transport y pase su nueva clase al método:

public class Airplane extends Transport { //implementation }

y rent(new Airplane()) . Y new Airplane().go() en el segundo caso.


Esta situación ocurre cuando tienes varias implementaciones. Dejame explicar. Supongamos que tiene varios algoritmos de clasificación y desea elegir en el tiempo de ejecución el que desea implementar, o si desea otorgar a otra persona la capacidad de agregar su implementación. Para resolver este problema, generalmente se crea una clase abstracta (Parent) y se implementa diferente (Child). Si tú escribes:

Child c = new Child();

vincula su implementación a la clase Child y ya no puede cambiarla. De lo contrario, si usa:

Parent p = new Child();

siempre que Child extienda Parent, puede cambiarlo en el futuro sin modificar el código.

Lo mismo se puede hacer usando interfaces: Parent ya no es una clase sino una interfaz java.

En general, puede utilizar este enfoque en el patrón DAO donde desee tener varias implementaciones dependientes de DB. Puede echar un vistazo a FactoryPatter o AbstractFactory Pattern. Espero que esto le pueda ayudar.


Le permite acceder a todas las subclases a través de una interfaz principal común. Esto es beneficioso para ejecutar operaciones comunes disponibles en todas las subclases. Se necesita un mejor ejemplo:

public class Shape { private int x, y; public void draw(); } public class Rectangle extends Shape { public void draw(); public void doRectangleAction(); }

Ahora si tienes:

List<Shape> myShapes = new ArrayList<Shape>();

Puede hacer referencia a cada elemento de la lista como una Forma, no tiene que preocuparse si es un Rectángulo o algún otro tipo, digamos Círculo. Puedes tratarlos todos igual; puedes dibujarlos todos. No puede llamar a doRectangleAction porque no sabe si la forma es realmente un rectángulo.

Esta es una transacción que hace entre tratar objetos de forma genérica y tratar específicamente.

Realmente creo que necesitas leer más sobre OOP. Un buen libro debería ayudar: http://www.amazon.com/Design-Patterns-Explained-Perspective-Object-Oriented/dp/0201715945


Primero, una aclaración de terminología: estamos asignando un objeto Child a una variable de tipo Parent . Parent es una referencia a un objeto que resulta ser un subtipo de Parent , a Child .

Solo es útil en un ejemplo más complicado. Imagine que agrega getEmployeeDetails a la clase Parent:

public String getEmployeeDetails() { return "Name: " + name; }

Podríamos anular ese método en Child para proporcionar más detalles:

@Override public String getEmployeeDetails() { return "Name: " + name + " Salary: " + salary; }

Ahora puede escribir una línea de código que obtenga los detalles que estén disponibles, ya sea que el objeto sea Parent o Child :

parent.getEmployeeDetails();

El siguiente código:

Parent parent = new Parent(); parent.name = 1; Child child = new Child(); child.name = 2; child.salary = 2000; Parent[] employees = new Parent[] { parent, child }; for (Parent employee : employees) { employee.getEmployeeDetails(); }

Dará como resultado la salida:

Name: 1 Name: 2 Salary: 2000

Usamos un Child como Parent . Tenía un comportamiento especializado único para la clase Child , pero cuando llamamos getEmployeeDetails() podríamos ignorar la diferencia y enfocarnos en cómo Parent e Child son similares. Esto se llama polimorfismo de subtipo .

Su pregunta actualizada pregunta por Child.salary no se puede acceder a Child.salary cuando el objeto Child se almacena en una referencia principal. La respuesta es la intersección de "polimorfismo" y "tipado estático". Debido a que Java está tipado estáticamente en tiempo de compilación, usted obtiene ciertas garantías del compilador, pero se ve obligado a seguir reglas a cambio o el código no se compilará. Aquí, la garantía relevante es que cada instancia de un subtipo (por ejemplo, Child ) se puede usar como una instancia de su supertipo (por ejemplo, Parent ). Por ejemplo, se le garantiza que cuando ingrese a employee.getEmployeeDetails o employee.name el método o campo se definirá en cualquier objeto no nulo que pueda asignarse a un employee variable del tipo Parent . Para hacer esta garantía, el compilador solo considera ese tipo estático (básicamente, el tipo de referencia de la variable, Parent ) cuando decide a qué puede acceder. Por lo tanto, no puede acceder a ningún miembro que esté definido en el tipo de tiempo de ejecución del objeto, Child .

Cuando realmente desee utilizar a un Child como Parent esta es una restricción fácil de vivir y su código será utilizable para el Parent y todos sus subtipos. Cuando eso no sea aceptable, cree el tipo de la referencia Child .


Si asigna el tipo padre a una subclase, significa que acepta usar las características comunes de la clase principal.

Te da la libertad de abstraer desde diferentes implementaciones de subclases. Como resultado, te limita con las funciones principales.

Sin embargo, este tipo de asignación se llama upcasting.

Parent parent = new Child();

Lo opuesto es downcasting.

Child child = (Child)parent;

Por lo tanto, si crea una instancia de Child y la baja a Parent , puede usar ese tipo de name atributo. Si crea una instancia de Parent , puede hacer lo mismo que en el caso anterior, pero no puede usar el salary porque no hay dicho atributo en el Parent . Regrese al caso anterior que puede usar el salary pero solo si lo envió a Child .

Hay más explicaciones de detalles


Supongamos que desea tener una matriz de instancias de clases para padres y un conjunto de clases para niños Child1, Child2, Child3 que extiende Parent. Hay situaciones en las que solo te interesa la implementación de la clase para padres, que es más general, y no te importan las cosas más específicas introducidas por las clases para niños.


Usted declara a los padres como padres, por lo que Java proporcionará solo los métodos y atributos de la clase para padres.

Child child = new Child();

Deberia trabajar. O

Parent child = new Child(); ((Child)child).salary = 1;