webentwicklung-frage-antwort-db.com.de

Welche überladene Methode wird in Java aufgerufen

Ich habe eine grundlegende Vererbungssituation mit einer überladenen Methode in der Superklasse. 

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

Die folgende Employee-Klasse erweitert die obige Person-Klasse:

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

Die Hauptmethode erstellt einfach ein Employee-Objekt (sowohl statischen als auch dynamischen Typ) und ruft .work() auf:

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

Dies endet mit dem Drucken

This person is not an Employee 

Beim Durchschauen hatte ich gedacht, da sowohl der statische als auch der dynamische Typ des Objekts e1Employee ist, würde es die überladene Methode in Person aufrufen, die eine Employee als Parameter verwendet. Da ich diesbezüglich eindeutig falsch bin, habe ich einen Debugger geöffnet, in dem der Verweis auf "this" in der Zeile getWorkDetail(this) in der Person-Klasse zu seiner Superklasse verwandelt werden muss. Dies habe ich jedoch nicht gefunden.

 screenshot

An dieser Stelle ist this eindeutig ein Employee-Objekt, jedoch hat es sich trotzdem entschieden, die überladene Methode getWorkDetail(Person p) auszuführen. Kann jemand dieses Verhalten erklären?

56
Eric S

Im Gegensatz zu Methodenüberschreibungen werden Methodenüberladungen basierend auf dem statischen Typ verknüpft. In diesem Fall weiß getWorkDetail(this) in Person nur den Typ Person.

Das Überladen von Methoden ist nicht dafür ausgelegt, ein dynamisches Laufzeitverhalten bereitzustellen.

Um die dynamische Bindung zu nutzen, müssen Sie möglicherweise Ihren Code umgestalten, um die Methoden zu überschreiben.

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

Und ändern Sie das Verhalten basierend auf der Implementierung von Klassen. Selbstverständlich können Sie Methoden überladen, sofern Sie auch die überladenen Methoden überschreiben, falls dies erforderlich ist.

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

Die Überlastauflösung erfolgt während der Kompilierzeit, nicht zur Laufzeit.

Wenn Sie also getWorkDetails(this) aufrufen, wird angenommen, dass this eine Person ist (was der statische Typ ist) und daher die entsprechende Überladung genannt wird.

Anmerkung: Die Verwendung von this in der Klasse Employee hätte einen Employee-Typ zur Folge. Sie können dies überprüfen, indem Sie work() in Employee wie folgt überladen.

class Employee extends Person {
    ...

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

Problemspezifische Lösung

In einigen Sprachen werden Parameter in ihren dynamischen Typ aufgelöst, jedoch nicht in Java. Der Compiler bestimmt bereits zur Kompilierzeit, wohin Ihre getWorkDetail(this); gehen soll. this ist vom Typ Person, daher wird getWorkDetail(Person e) aufgerufen. In Ihrem speziellen Fall liegt die Lösung auf der Hand. Wie bereits erwähnt, müssen Sie getWorkDetail() in der Employee-Klasse überschreiben.

Auflösen von Methoden in ihre dynamischen Parametertypen

Um das allgemeine Problem der Auflösung von Parametertypen zur Laufzeit zu lösen, sollte der Operator instanceof vermieden werden, da dies in der Regel zu unsauberem Code führt.

Wenn Sie zwei verschiedene Klassen haben, ist eine so einfache Lösung wie oben beschrieben nicht mehr möglich. In diesen Fällen müssen Sie das Besuchermuster verwenden.

Betrachten Sie die folgenden Klassen:

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

Das kannst du so nennen:

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

Nach dem visitor-Muster wird beim Aufruf von Animal.eatFood.eatenBy aufgerufen, das von Meat und Vegetables implementiert wird. Diese Klassen rufen die spezifischere eatMeat- oder eatVegetables-Methode auf, die die richtigen (dynamischen) Typen verwendet.

7
Lonely Neuron

Anrufpräferenz

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

Die Ausgabe wird auf der Konsole int gedruckt. Jetzt kommentieren Sie die erste test()-Methode und sehen, welche Ausgabe kommt.

Dies ist die Präferenzhirarchey in primitiven Datentypen. Wenn Sie jetzt zu abgeleiteten Typen kommen, deklarieren Sie eine Klasse FooChild wie folgt

class FooChild extends Foo {

}

und erstellen Sie zwei neue Methoden in Foo like

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

dann versuchen Sie in der Hauptmethode, testChild wie folgt testChild(new FooChild()); aufzurufen.

0
Arun Sudhakaran