web-dev-qa-db-fra.com

Quelle méthode surchargée est appelée en Java

J'ai une situation d'héritage de base avec une méthode surchargée dans la super classe. 

public class Person {
    private String name;
    private int dob;
    private String gender;

    public Person(String theName, int birth, String sex){
        name = theName;
        dob = birth;
        gender = sex;
    }

    public void work(){
        getWorkDetail(this);
    }

    public void getWorkDetail(Employee e){
        System.out.println("This person is an Employee");
    }

    public void getWorkDetail(Person p){
        System.out.println("This person is not an Employee");
    }
}

La classe Employee suivante étend la classe Person ci-dessus:

public class Employee extends Person {

    String department;
    double salary;

    public Employee(String theName, int birth, String sex){
        super(theName, birth, sex);
        department = "Not assigned";
        salary = 30000;
    }
}

La méthode principale crée simplement un objet Employee (de type statique et dynamique) et y appelle .work():

public static void main(String[] args){
    Employee e1 = new Employee("Manager1", 1976, "Female");
    e1.work();
}

Cela finit par imprimer

This person is not an Employee 

En regardant à travers cela, j'avais pensé que puisque le type statique et dynamique de l'objet e1 était Employee, il appellerait la méthode surchargée dans Person qui prend un Employee en paramètre. Puisque je me trompe clairement à ce sujet, j'ai ouvert un débogueur en supposant que la référence à "this" à la ligne getWorkDetail(this) dans la classe Person doit s'être transformée en super classe. Cependant ce n'est pas ce que j'ai trouvé.

 screenshot

Clairement, à ce stade du code, this est un objet Employee, mais il a quand même choisi d'exécuter la méthode surchargée getWorkDetail(Person p). Quelqu'un peut-il expliquer ce comportement?

56
Eric S

Contrairement aux substitutions de méthodes, les surcharges de méthodes sont liées en fonction du type statique. Et dans ce cas, getWorkDetail(this) dans Person ne connaît que le type Person.

La surcharge de méthode n'est pas conçue pour fournir un comportement d'exécution dynamique.

Pour tirer parti de la liaison dynamique, vous devrez peut-être repenser votre code pour remplacer les méthodes, à la place:

public static void main(String[] args) throws IOException {
    new Employee("Manager1", 1976, "Female").getWorkDetail();
    new Person("Manager1", 1976, "Female").getWorkDetail();
}

Et modifier le comportement en fonction de la mise en œuvre des classes. Bien sûr, vous pouvez surcharger des méthodes, à condition de vous en occuper également, le cas échéant.

class Person {
    private String name;
    private int dob;
    private String gender;

    public Person(String theName, int birth, String sex) {
        name = theName;
        dob = birth;
        gender = sex;
    }

    public void getWorkDetail() {
        System.out.println("This person is not an Employee");
    }
}

class Employee extends Person {

    String department;
    double salary;

    public Employee(String theName, int birth, String sex) {
        super(theName, birth, sex);
        department = "Not assigned";
        salary = 30000;
    }

    public void getWorkDetail() {
        System.out.println("This person is an Employee");
    }
}
69
ernest_k

La résolution de surcharge se produit pendant la compilation, pas au moment de l'exécution.

Ainsi, lorsque vous appelez getWorkDetails(this), this est supposé être un Person (qui est le type statique) et est donc appelé surcharge correspondante.

Remarque: L'utilisation de this dans la classe Employee en aurait fait un type Employee. Vous pouvez le vérifier en surchargeant work() dans Employee comme ceci.

class Employee extends Person {
    ...

    public void work() {
        getWorkDetails(this); // This should print "This person is an Employee"
    }
}
23
Codebender

Solution spécifique au problème

Dans certains langages, les paramètres sont résolus en leur type dynamique, mais pas en Java. Le compilateur détermine déjà au moment de la compilation où votre getWorkDetail(this); ira. this est de type Person, donc getWorkDetail(Person e) est appelé. Dans votre cas particulier, la solution est assez évidente. Comme d'autres l'ont déjà souligné, vous devrez remplacer getWorkDetail() dans la classe Employee.

Résolution des méthodes sur leurs types de paramètres dynamiques

Pour résoudre le problème général de la résolution des types de paramètres au moment de l'exécution, il convient d'éviter d'utiliser l'opérateur instanceof, car cela conduit généralement à un code non propre.

Si vous avez deux classes différentes, une solution aussi simple que celle indiquée ci-dessus n'est plus possible. Dans ces cas, vous devrez utiliser le modèle visiteur .

Considérez les classes suivantes:

public interface Animal {
    default void eat(Food food) {
        food.eatenBy(this);
    }

    void eatMeat(Meat meat);

    void eatVegetables(Vegetables vegetables);
}

public class Shark implements Animal {
    public void eatMeat (Meat food) {
        System.out.println("Tasty meat!");
    }

    public void eatVegetables (Vegetables food) {
        System.out.println("Yuck!");
    }
}

public interface Food {
    void eatenBy(Animal animal);
}

public class Meat implements Food {
    public void eatenBy(Animal animal) {
        animal.eatMeat(this);
    }
}

public class Vegetables implements Food {
    public void eatenBy(Animal animal) {
        animal.eatVegetables(this);
    }
}

Que vous pouvez appeler comme ça:

Animal animal = new Shark();
Food someMeat = new Meat();
Food someVegetables= new Vegetables();
animal.eat(someMeat);        // prints "Tasty meat!"
animal.eat(someVegetables);  // prints "Yuck!"

En suivant le modèle visitor, l'appel de Animal.eat appelle Food.eatenBy, qui est implémenté à la fois par Meat et Vegetables. Ces classes appellent la méthode plus spécifique eatMeat ou eatVegetables, qui utilise les types corrects (dynamiques).

7
Lonely Neuron

Préférence d'appel

class Foo {
    static void test(int arg) { System.out.println("int"); }
    static void test(float arg) { System.out.println("float"); }
    static void test(Integer arg) { System.out.println("Integer"); }
    static void test(int... arg) { System.out.println("int..."); }

    public static void main(String[] arg) {
        test(6);
    }
}

La sortie sera int imprimée sur la console. Maintenant, commentez la première méthode test() et voyez quelle sera la sortie.

C'est la préférence hirarchey dans les types de données primitifs. Venant maintenant aux types dérivés, déclare une classe FooChild comme ceci

class FooChild extends Foo {

}

et créez deux nouvelles méthodes dans Foo comme

static void testChild(Foo foo) { System.out.println("Foo"); }
static void testChild(FooChild fooChild) { System.out.println("FooChild"); }

puis dans la méthode principale, essayez d'appeler testChild comme ceci testChild(new FooChild());.

0
Arun Sudhakaran