106 votes

Pourquoi assigne-t-on une référence parentale à l'objet enfant en Java ?

Je pose une question assez simple, mais je suis un peu confus.

Supposons que j'ai une classe Parent :

public class Parent {

    int name;
}

Et avoir une autre classe Child :

public class Child extends Parent{

    int salary;
}

Et enfin ma classe Main.java

public class Main {

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

Si je crée un objet enfant comme

Child child = new Child():

Puis child peut accéder aux deux name and salary variables.

Ma question est la suivante :

Parent parent = new Child();

donne l'accès de seulement name de la classe Parent. Quelle est donc l'utilité exacte de cette ligne ?

 Parent parent = new Child();

Et aussi quand on utilise le polymorphisme dynamique, pourquoi la variable de la classe enfant n'est pas accessible après avoir fait cela ?

Parent parent = new Child();

74voto

John Watts Points 5349

Tout d'abord, une clarification de la terminologie : nous attribuons une Child à une variable de type Parent . Parent est une référence à un objet qui se trouve être un sous-type de Parent , a Child .

Elle n'est utile que dans un exemple plus compliqué. Imaginez que vous ajoutez getEmployeeDetails à la classe Parent :

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

Nous pourrions surcharger cette méthode dans Child pour fournir plus de détails :

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

Maintenant, vous pouvez écrire une ligne de code qui obtient tous les détails disponibles, que l'objet soit une Parent o Child :

parent.getEmployeeDetails();

Le code suivant :

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();
}

Le résultat sera la sortie :

Name: 1
Name: 2 Salary: 2000

Nous avons utilisé un Child en tant que Parent . Il avait un comportement spécialisé propre à la Child mais lorsque nous avons appelé getEmployeeDetails() nous pourrions ignorer la différence et nous concentrer sur la façon dont Parent y Child sont similaires. C'est ce qu'on appelle polymorphisme de sous-type .

Votre question actualisée demande pourquoi Child.salary n'est pas accessible lorsque le Child est stocké dans un Parent référence. La réponse est l'intersection du "polymorphisme" et du "typage statique". Parce que Java est typée statiquement au moment de la compilation, vous obtenez certaines garanties du compilateur, mais vous êtes obligé de suivre des règles en échange, sinon le code ne sera pas compilé. Ici, la garantie pertinente est que chaque instance d'un sous-type (par ex. Child ) peut être utilisé comme une instance de son supertype (par ex. Parent ). Par exemple, vous avez la garantie que lorsque vous accédez à employee.getEmployeeDetails o employee.name la méthode ou le champ est défini sur tout objet non nul qui pourrait être affecté à une variable employee de type Parent . Pour faire cette garantie, le compilateur ne considère que le type statique (en gros, le type de la référence de la variable, Parent ) pour décider de ce à quoi vous pouvez accéder. Vous ne pouvez donc pas accéder aux membres qui sont définis sur le type d'exécution de l'objet, Child .

Lorsque vous voulez vraiment utiliser un Child en tant que Parent Il s'agit d'une restriction facile à accepter et votre code sera utilisable pour les applications suivantes Parent et tous ses sous-types. Lorsque cela n'est pas acceptable, faites en sorte que le type de la référence Child .

30voto

Akshat Chaturvedi Points 540

Lorsque vous compilez votre programme, la variable de référence de la classe de base est mémorisée et le compilateur vérifie toutes les méthodes de cette classe. Il vérifie donc toutes les méthodes de la classe de base mais pas celles de la classe enfant. Maintenant, au moment de l'exécution, lorsque l'objet est créé, seules les méthodes vérifiées peuvent être exécutées. Si une méthode est surchargée dans la classe enfant, cette fonction s'exécute. Les autres fonctions de la classe enfant ne sont pas exécutées parce que le compilateur ne les a pas reconnues au moment de la compilation.

16voto

anio Points 3254

Elle vous permet d'accéder à toutes les sous-classes par le biais d'une interface parent commune. Ceci est bénéfique pour exécuter des opérations communes disponibles sur toutes les sous-classes. Un meilleur exemple est nécessaire :

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

public class Rectangle extends Shape
{ 
  public void draw();
  public void doRectangleAction();
}

Maintenant, si vous avez :

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

Vous pouvez référencer chaque élément de la liste en tant que forme, sans vous préoccuper de savoir s'il s'agit d'un rectangle ou d'un autre type, par exemple un cercle. Vous pouvez les traiter tous de la même manière, vous pouvez les dessiner tous. Vous ne pouvez pas appeler doRectangleAction parce que vous ne savez pas si la forme est vraiment un rectangle.

Il s'agit d'un échange que vous faites entre traiter les objets de manière générique et les traiter de manière spécifique.

Vraiment, je pense que vous avez besoin de lire plus sur la POO. Un bon livre devrait vous aider : http://www.amazon.com/Design-Patterns-Explained-Perspective-Object-Oriented/dp/0201715945

13voto

Roman C Points 18658

Si vous attribuez le type parent à une sous-classe, cela signifie que vous acceptez d'utiliser les caractéristiques communes de la classe parent.

Cela vous donne la liberté de faire abstraction de différentes implémentations de sous-classes. En conséquence, vous êtes limité par les fonctions parentales.

Toutefois, ce type d'affectation est appelé "upcasting".

Parent parent = new Child();  

L'opposé est le downcasting.

Child child = (Child)parent;

Ainsi, si vous créez une instance de Child et l'abaisser à Parent vous pouvez utiliser cet attribut de type name . Si vous créez une instance de Parent vous pouvez faire la même chose qu'avec le cas précédent mais vous ne pouvez pas utiliser salary parce qu'il n'y a pas de tel attribut dans le Parent . Revenons au cas précédent qui peut utiliser salary mais uniquement en cas de diffusion descendante vers Child .

Il y a une explication plus détaillée

8voto

kapelchik Points 2461

C'est simple.

Parent parent = new Child();

Dans ce cas, le type de l'objet est Parent . Fourmi Parent n'a qu'une seule propriété. Il s'agit de name .

Child child = new Child();

Et dans ce cas, le type de l'objet est Child . Fourmi Child a deux propriétés. Ce sont name y salary .

Le fait est qu'il n'est pas nécessaire d'initialiser les champs non finaux immédiatement lors de la déclaration. Habituellement, cela est fait au moment de l'exécution parce que souvent vous ne pouvez pas savoir exactement de quelle implémentation exacte vous aurez besoin. Par exemple, imaginez que vous avez une hiérarchie de classes avec la classe Transport à la tête. Et trois sous-classes : Car , Helicopter y Boat . Et il y a une autre classe Tour qui a pour champ Transport . C'est-à-dire :

class Tour {
   Transport transport;
}  

Tant qu'un utilisateur n'a pas réservé un voyage et n'a pas choisi un type de transport particulier, vous ne pouvez pas initialiser ce champ. C'est le premier.

Ensuite, supposons que toutes ces classes doivent avoir une méthode go() mais avec une mise en œuvre différente. Vous pouvez définir une implémentation de base par défaut dans la superclasse Transport et leurs propres implémentations uniques dans chaque sous-classe. Avec cette initialisation Transport tran; tran = new Car(); vous pouvez appeler la méthode tran.go() et obtenir le résultat sans se soucier de la mise en œuvre spécifique. Il appellera une méthode surchargée d'une sous-classe particulière.

De plus, vous pouvez utiliser l'instance de la sous-classe partout où l'instance de la super-classe est utilisée. Par exemple, vous voulez offrir la possibilité de louer votre transport. Si vous n'utilisez pas le polymorphisme, vous devez écrire beaucoup de méthodes pour chaque cas : rentCar(Car car) , rentBoat(Boat boat) et ainsi de suite. En même temps, le polymorphisme vous permet de créer une méthode universelle rent(Transport transport) . Vous pouvez lui passer un objet de n'importe quelle sous-classe de Transport . De plus, si au fil du temps votre logique augmente et que vous devez créer une autre classe dans la hiérarchie ? Lorsque vous utilisez le polymorphisme, vous n'avez pas besoin de changer quoi que ce soit. Il suffit d'étendre la classe Transport et passez votre nouvelle classe dans la méthode :

public class Airplane extends Transport {
    //implementation
}

y rent(new Airplane()) . Et new Airplane().go() dans le second cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X