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
:
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 infantil como
Child child = new Child():
Entonces el child
objeto puede acceder a ambas name and salary
variables.
Mi pregunta es:
Parent parent = new Child();
da acceso a la única name
variable de la clase Parent. Entonces, ¿cuál es el uso exacto de esta línea?
Parent parent = new Child();
Y también cuando se usa polimorfismo dinámico, ¿por qué la variable de la clase secundaria no es accesible después de hacer esto?
Parent parent = new Child();
name
tipo correcto?Respuestas:
Primero, una aclaración de la terminología: estamos asignando un
Child
objeto a una variable de tipoParent
.Parent
es una referencia a un objeto que resulta ser un subtipo deParent
, aChild
.Solo es útil en un ejemplo más complicado. Imagina que agregas
getEmployeeDetails
a la clase Parent:public String getEmployeeDetails() { return "Name: " + name; }
Podríamos anular ese método
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 disponibles, ya sea que el objeto sea un
Parent
oChild
: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 unParent
. Tenía un comportamiento especializado exclusivo de laChild
clase, pero cuandogetEmployeeDetails()
llamamos podíamos ignorar la diferencia y centrarnos en cómoParent
yChild
son similares. Esto se llama polimorfismo de subtipo .Su pregunta actualizada pregunta por qué
Child.salary
no es accesible cuando elChild
objeto está almacenado en unaParent
referencia. La respuesta es la intersección de "polimorfismo" y "tipificación estática". Debido a que Java se escribe estáticamente en el momento de la compilación, 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 (pChild
. Ej. ) Puede usarse como una instancia de su supertipo (pParent
. Ej .). Por ejemplo, se le garantiza que cuando accedeemployee.getEmployeeDetails
oemployee.name
el método o campo se define en cualquier objeto no nulo que podría asignarse a una variableemployee
de tipoParent
. ., Para hacer esta garantía, el compilador considera solo ese tipo estático (básicamente, el tipo de referencia de variableParent
) al decidir a qué puede acceder. Por lo tanto, no puede acceder a ningún miembro definido en el tipo de tiempo de ejecución del objeto,Child
.Cuando realmente desee usar un
Child
como,Parent
esta es una restricción fácil de usar y su código se podrá usar paraParent
todos sus subtipos. Cuando eso no sea aceptable, escriba el tipo de referenciaChild
.fuente
Cuando compila su programa, la variable de referencia de la clase base obtiene memoria y el compilador verifica todos los métodos en esa clase. Por lo tanto, verifica todos los métodos de la clase base pero no los métodos de la clase secundaria. Ahora, en tiempo de ejecución, cuando se crea el objeto, solo se pueden ejecutar los métodos marcados. En caso de que se anule un método en la clase secundaria, esa función se ejecuta. Las otras funciones de la clase secundaria no se ejecutan porque el compilador no las ha reconocido en el momento de la compilación.
fuente
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 como, por ejemplo, círculo. Puedes tratarlos de todos modos; puedes dibujarlos todos. No puede llamar a doRectangleAction porque no sabe si la forma es realmente un rectángulo.
Este es un intercambio que haces entre tratar los objetos de una manera genérica y tratar los objetos 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
fuente
Si asigna un tipo principal a una subclase, significa que está de acuerdo con utilizar las características comunes de la clase principal.
Le da la libertad de abstraerse de diferentes implementaciones de subclase. Como resultado, lo limita con las funciones principales.
Sin embargo, este tipo de asignación se denomina upcasting.
Parent parent = new Child();
Lo contrario es abatimiento.
Por lo tanto, si crea una instancia de
Child
y la rebaja,Parent
puede usar ese atributo de tiponame
. Si crea una instancia deParent
, puede hacer lo mismo que con el caso anterior, pero no puede usarlosalary
porque no existe ese atributo en elParent
. Regrese al caso anterior que puede usarsalary
pero solo si se baja aChild
.Hay una explicación más detallada
fuente
Parent
puede usarParent
si asigna (si puede)Child
puede usarChild
.Es simple.
Parent parent = new Child();
En este caso, el tipo de objeto es
Parent
. AntParent
tiene solo una propiedad. Esname
.Child child = new Child();
Y en este caso el tipo de objeto es
Child
. AntChild
tiene dos propiedades. Sonname
ysalary
.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 necesitará. Por ejemplo, imagina que tienes una jerarquía de clases con la clase
Transport
a la cabeza. Y tres subclases:Car
,Helicopter
yBoat
. Y hay otra claseTour
que tiene campoTransport
. Es decir:class Tour { Transport transport; }
Mientras un usuario no haya reservado un viaje y no haya elegido un tipo de transporte en particular, no puede inicializar este campo. Es primero.
En segundo lugar, 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 la superclaseTransport
y poseer implementaciones únicas en cada subclase. Con esta inicializaciónTransport tran; tran = new Car();
puedes llamar al métodotran.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 cualquier lugar donde se use una instancia de superclase. Por ejemplo, desea brindar la oportunidad de alquilar su transporte. Si no se utiliza el polimorfismo, usted tiene que escribir una gran cantidad de métodos para cada caso:
rentCar(Car car)
,rentBoat(Boat boat)
y así sucesivamente. Al mismo tiempo, el polimorfismo le permite crear un método universalrent(Transport transport)
. Puede pasar en él objeto de cualquier subclase deTransport
. Además, si con el tiempo su lógica aumentará y necesitará crear otra clase en la jerarquía. Cuando se usa polimorfismo, no es necesario cambiar nada. Simplemente extienda la claseTransport
y pase su nueva clase al método:public class Airplane extends Transport { //implementation }
y
rent(new Airplane())
. Ynew Airplane().go()
en el segundo caso.fuente
Esta situación ocurre cuando tienes varias implementaciones. Dejame explicar. Suponga que tiene varios algoritmos de clasificación y desea elegir en tiempo de ejecución el que desea implementar, o desea darle a otra persona la capacidad de agregar su implementación. Para resolver este problema, generalmente crea una clase abstracta (Padre) y tiene una implementación diferente (Niño). Si tú escribes:
Child c = new Child();
enlaza su implementación a la clase Child y ya no puede cambiarla. De lo contrario, si usa:
Parent p = new Child();
Siempre que Child amplíe Parent, puede cambiarlo en el futuro sin modificar el código.
Se puede hacer lo mismo usando interfaces: Parent ya no es una clase, sino una interfaz java.
En general, puede usar este enfoque en el patrón DAO donde desea tener varias implementaciones dependientes de DB. Puede echar un vistazo a FactoryPatter o AbstractFactory Pattern. Espero que esto le pueda ayudar.
fuente
Digamos que le gustaría tener una matriz de instancias de la clase Parent y un conjunto de clases secundarias Child1, Child2, Child3 extendiendo Parent. Hay situaciones en las que solo está interesado en la implementación de la clase principal, que es más general, y no le importan las cosas más específicas introducidas por las clases secundarias.
fuente
Creo que todas las explicaciones anteriores son un poco demasiado técnicas para las personas que son nuevas en la programación orientada a objetos (OOP). Hace años, me tomó un tiempo entender esto (como Jr Java Developer) y realmente no entendía por qué usamos una clase principal o una interfaz para ocultar la clase real a la que realmente estamos llamando bajo las sábanas.
La razón inmediata por la que es para ocultar la complejidad, para que la persona que llama no necesite cambiar con frecuencia (ser pirateado y secuestrado en términos sencillos). Esto tiene mucho sentido, especialmente si su objetivo es evitar la creación de errores. Y cuanto más modifiques el código, más probable será que algunos de ellos se te acerquen. Por otro lado, si solo extiende el código, es menos probable que tenga errores porque se concentra en una cosa a la vez y su código anterior no cambia o cambia solo un poco. Imagina que tienes una aplicación sencilla que permite a los empleados de la profesión médica crear perfiles. Para simplificar, supongamos que solo tenemos médicos generales, cirujanos y enfermeras (en realidad, hay muchas profesiones más específicas, por supuesto). Para cada profesión, desea almacenar información general y alguna específica solo para ese profesional. Por ejemplo, un cirujano puede tener campos generales como firstName, lastName, yearsOfExperience como campos generales, pero también campos específicos, por ejemplo, especializaciones almacenadas en una variable de instancia de lista, como Lista con contenido similar a "Cirugía ósea", "Cirugía ocular", etc. Una enfermera no tendría nada de eso, pero puede tener una lista de procedimientos con los que esté familiarizada, GeneralPractioners tendría sus propios detalles. Como resultado, cómo se guarda un perfil de un específico. Sin embargo, no desea que su clase de ProfileManager conozca estas diferencias, ya que inevitablemente cambiarán y aumentarán con el tiempo a medida que su aplicación amplíe su funcionalidad para cubrir más profesiones médicas, por ejemplo, fisioterapeuta, cardiólogo, oncólogo, etc. Todo lo que quiere que haga su ProfileManger es decir guardar (), sin importar de quién sea el perfil que esté guardando. Por lo tanto, es una práctica común ocultar esto detrás y la Interfaz y la Clase abstracta o una Clase principal (si planea permitir la creación de un empleado médico general). En este caso, elijamos una clase principal y la llamamos MedicalEmployee. Debajo de las cubiertas, puede hacer referencia a cualquiera de las clases específicas anteriores que lo amplían. Cuando ProfileManager llama a myMedicalEmployee.save (), el método save () se resolverá polimórficamente (muchas estructuras) en el tipo de clase correcto que se usó para crear el perfil originalmente, por ejemplo Nurse y llamará al método save () en ese clase. o una Clase para padres (si planea permitir la creación de un empleado médico general). En este caso, elijamos una clase principal y la llamamos MedicalEmployee. Debajo de las cubiertas, puede hacer referencia a cualquiera de las clases específicas anteriores que lo amplían. Cuando ProfileManager llama a myMedicalEmployee.save (), el método save () se resolverá polimórficamente (muchas estructuras) en el tipo de clase correcto que se usó para crear el perfil originalmente, por ejemplo Nurse y llamará al método save () en ese clase. o una Clase para padres (si planea permitir la creación de un empleado médico general). En este caso, elijamos una clase principal y la llamamos MedicalEmployee. Debajo de las cubiertas, puede hacer referencia a cualquiera de las clases específicas anteriores que lo amplían. Cuando ProfileManager llama a myMedicalEmployee.save (), el método save () se resolverá polimórficamente (muchas estructuras) en el tipo de clase correcto que se usó para crear el perfil originalmente, por ejemplo Nurse y llamará al método save () en ese clase.
En muchos casos, no sabe realmente qué implementación necesitará en tiempo de ejecución. En el ejemplo anterior, no tiene idea de si un médico general, un cirujano o una enfermera crearían un perfil. Sin embargo, sabe que debe guardar ese perfil una vez completado, pase lo que pase. MedicalEmployee.profile () hace exactamente eso. Es replicado (anulado) por cada tipo específico de Empleado Médico: Médico General, Cirujano, Enfermera,
El resultado de (1) y (2) anteriores es que ahora puede agregar nuevas profesiones médicas, implementar save () en cada nueva clase, anulando así el método save () en MedicalEmployee, y no tiene que modificar ProfileManager en todas.
fuente
Sé que este es un hilo muy antiguo, pero una vez me encontré con la misma duda.
Entonces, el concepto de
Parent parent = new Child();
tiene algo que ver con el enlace temprano y tardío en java.La vinculación de métodos privados, estáticos y finales ocurre en la compilación, ya que no se pueden anular y las llamadas a métodos normales y los métodos sobrecargados son ejemplos de vinculación temprana.
Considere el ejemplo:
class Vehicle { int value = 100; void start() { System.out.println("Vehicle Started"); } static void stop() { System.out.println("Vehicle Stopped"); } } class Car extends Vehicle { int value = 1000; @Override void start() { System.out.println("Car Started"); } static void stop() { System.out.println("Car Stopped"); } public static void main(String args[]) { // Car extends Vehicle Vehicle vehicle = new Car(); System.out.println(vehicle.value); vehicle.start(); vehicle.stop(); } }
Salida: 100
Coche arrancado
Vehículo detenido
Esto sucede porque
stop()
es un método estático y no se puede anular. Por lo tanto, la vinculaciónstop()
ocurre en tiempo de compilación ystart()
no es estática y se anula en la clase secundaria. Por lo tanto, la información sobre el tipo de objeto está disponible solo en el tiempo de ejecución (enlace tardío) y, por lo tanto, elstart()
, se llama método de la clase Car.También en este código
vehicle.value
nos da100
como salida porque la inicialización de la variable no viene bajo enlace tardío. La invalidación de métodos es una de las formas en que Java admite el polimorfismo en tiempo de ejecución .Espero que esto responda dónde
Parent parent = new Child();
es importante y también por qué no pudo acceder a la variable de clase secundaria usando la referencia anterior.fuente
por ejemplo tenemos un
class Employee { int getsalary() {return 0;} String getDesignation() { return “default”; } } class Manager extends Employee { int getsalary() { return 20000; } String getDesignation() { return “Manager” } } class SoftwareEngineer extends Employee { int getsalary() { return 20000; } String getDesignation() { return “Manager” } }
ahora, si desea establecer u obtener el salario y la designación de todos los empleados (es decir, ingeniero de software, gerente, etc.)
tomaremos una matriz de Empleado y llamaremos a ambos métodos getsalary (), getDesignation
Employee arr[]=new Employee[10]; arr[1]=new SoftwareEngieneer(); arr[2]=new Manager(); arr[n]=……. for(int i;i>arr.length;i++) { System.out.println(arr[i].getDesignation+””+arr[i].getSalary()) }
ahora es una especie de acoplamiento suelto porque puede tener diferentes tipos de empleados, por ejemplo: ingeniero de software, gerente, recursos humanos, empleado de despensa, etc.
para que pueda dar un objeto a la referencia principal independientemente de los diferentes objetos de empleado
fuente
Declara parent como Parent, por lo que java proporcionará solo métodos y atributos de la clase Parent.
Child child = new Child();
Deberia trabajar. O
Parent child = new Child(); ((Child)child).salary = 1;
fuente
Parent parent = new Child();
sea útil / significativo.