2010-01-27 9 views
3

je suis arrivé ce à partir d'une diffusion sur le Web des structures de données Berkley cs:Question sur le typage dynamique en Java

class A { 
    void f() {System.out.println("A.f");} 
    void g() {f();} 
// static void g(A y) {y.f();} 
} 

class B extends A { 
    void f(){ 
    System.out.println("B.f"); 
    } 
} 

class C { 
    static void main (String[] args){ 
    B aB = new B(); 
    h (aB); 
    } 

    static void h (A x) {x.g();} 
    //static void h (A x) {A.g(x);} what if this were h 

} 

Pouvez-vous me dire ce qui imprime et pourquoi? L'instructeur a dit B.f mais je ne comprends pas pourquoi. Je pensais que c'était A.f. Merci (non, je ne suis pas dans la classe, j'essaie juste d'apprendre.)

edit: désolé pour les erreurs que je copiais à partir d'une conférence vidéo.

+1

ce qui n'est pas un typage dynamique, il surcharge l'héritage . –

+0

Ce code Java n'est pas valide. Il existe deux méthodes statiques nommées 'h' avec la même signature. – erickson

+0

Je l'ai réparé. Merci. floue, c'est juste ce que le prof. appelé. Je n'ai vraiment aucune idée et a pris sa parole pour cela. – johnny

Répondre

1

La raison pour laquelle "B.f" imprime est que l'implémentation d'une méthode est déterminée par le type d'exécution d'un objet, et non par son type de compilation. C'est comme le mot-clé virtual en C++.

Une fois que vous construisez un B, vous savez que l'appel de sa méthode f imprimera « B.f », même si le B est fait référence comme A.

En outre, votre méthode A.f manque un accolade étroite.

+1

alors je crois que c'est le (A x) qui me trompe. La méthode de la classe c statique void h (A x) peut utiliser A comme type de paramètre parce que B est un A. mais cela n'a vraiment rien à voir avec le fait que je puisse encore passer un B construit. Est-ce vrai? – johnny

+0

Oui, c'est vrai. Une méthode qui attend un 'A' peut recevoir une instance de n'importe quel sous-type' S' de 'A'. Si 'S' remplace une méthode que' A' définit, alors c'est l'implémentation qui sera utilisée. – danben

1

ce code n'est pas correct, A.g() ne prend aucun argument.

static void h (A x) {A.g(x);} 

A.g (x); essaie d'appeler une méthode statique sur A appelée g avec x comme argument. Cela n'est pas possible avec l'exemple de code que vous avez publié.

autre que le code incorrect, la raison est B remplace la méthode f() avec sa propre implémentation. Ainsi, toutes les instances de B telles que B x = new B(); appellera le f() que B ne définit pas le A.f(). La seule façon d'atteindre le A.f() serait de l'intérieur d'une instance de B en appelant super.f();

+0

ce n'était pas au début ... mon erreur a été corrigé. – johnny

+0

ce n'était pas quand j'ai écrit la réponse –

+0

Juste pour clarifier: Il y a deux erreurs dans cette ligne de code. La première est que A.g ne prend aucun argument. La seconde est que g n'est pas une fonction statique, et doit donc être appelée avec un objet, pas un nom de classe. Le compilateur va lancer ça. – Jay

2

Cet exemple illustre la puissance de la programmation orientée objet. Comme ab est une instance de B, tout appel de méthode sur ab utilisera les fonctions définies dans B, même si ces fonctions sont appelées indirectement via une fonction définie dans une superclasse.

L'exemple est si abstrait que la vertu de ceci peut ne pas être claire. Permettez-moi un exemple un peu plus réaliste:

class Employee 
{ 
    ... bunch of stuff ... 
    void calcPay() 
    { 
    pay=hoursWorked*hourlyRate; 
    } 
    void produceCheck)) 
    { 
    calcPay(); 
    calcTaxes(); 
    calcBenefitDeductions(); 
    printCheck(); 
    } 
} 
class Salesman extends Employee 
{ 
    void calcPay() 
    { 
    pay=sales*commissionRate; 
    } 
} 
... somewhere else ... 
for (Employee employee1 : employeeList) 
{ 
    employee1.produceCheck(); 
} 

Je laisse toutes sortes de détails pour faire le point: Ce code ne compilera pas.

Mais voici le point: vendeur ont une méthode différente de calcul de leur salaire, puis les autres employés: Ils sont payés à la commission au lieu de l'heure. (Dans la vraie vie, nous aurions probablement aussi des employés, mais comme je le dis, je simplifie.) La fonction de calcul du salaire de l'un ou l'autre type d'employé est appelée à partir d'une fonction plus importante. La beauté de la programmation orientée objet est que nous pouvons appeler la fonction extérieure et ne pas nous soucier de savoir si l'objet contre lequel nous l'appelons est un employé régulier ou un vendeur. Chaque objet sait ce que c'est et appelle la bonne fonction. Dans les dernières lignes de l'exemple, nous avons une structure qui inclut à la fois les employés réguliers et les vendeurs, et nous pouvons les parcourir et les traiter sans avoir à vérifier leur type. Sans POO, nous aurions à écrire constamment du code comme: "if (type == SALESMAN) ... else if (type == HOURLY) ..."

+1

+1. Bon, mais sachez que c'est une meilleure pratique de ne pas instancier des classes non-feuille, juste comme un conseil pour les débutants. Donc une meilleure conception ici pourrait être de faire abstraction de Employee et calcPay(), et de sous-classer à la fois (par exemple) HourlyEmployee et Salesman. Oui, tel que présenté, cela fonctionnerait, mais vous évitez les problèmes si vous n'instanciez pas les classes non-feuille. –

+0

Je suis d'accord. En effet, j'ai commencé à construire l'exemple avec une classe abstraite et deux sous-classes, mais j'ai ensuite décidé qu'il était inutile d'ajouter de la complexité pour faire le point. Bien que je ne fasse pas une règle absolue, c'est certainement une bonne règle, et dans ce cas, dans la vie réelle, il serait certainement préférable de rendre l'employé abstrait comme vous le dites. – Jay