Какой перегруженный метод вызывается в Java

У меня есть базовая ситуация наследования с перегруженным методом в суперклассе.

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

Следующий класс Employee расширяет класс Person выше:

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

Основной метод просто создает объект Employee (как статический, так и динамический) и вызывает на нем функции .work():

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

Это заканчивается печатью

This person is not an Employee

Просматривая это, я подумал, что поскольку статический и динамический тип объекта e1 является Employee он вызывает перегруженный метод в Person, который принимает Employee в качестве параметра. Поскольку я явно ошибаюсь в этом, я открыл отладчик, предполагая, что ссылка на "this" в строке getWorkDetail(this) в классе Person должна была превратиться в суперкласс. Однако это не то, что я нашел.

screenshot

Очевидно, что в этот момент кода this объект Employee, однако он все же решил выполнить перегруженный метод getWorkDetail(Person p). Может ли кто-нибудь объяснить это поведение?

Ответ 1

В отличие от переопределения методов, перегрузки методов связаны на основе статического типа. И в этом случае getWorkDetail(this) в Person знает только о типе Person.

Перегрузка метода не предназначена для обеспечения динамического поведения во время выполнения.

Чтобы воспользоваться преимуществами динамической привязки, вам может потребоваться изменить свой код, чтобы переопределить методы, вместо этого:

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

И изменить поведение, основанное на реализации классов. Конечно, вы можете перегружать методы, если вы позаботитесь об переопределении перегруженных методов, если это необходимо.

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

Ответ 2

Разрешение перегрузки происходит во время компиляции, а не во время выполнения.

Так что, когда вы звоните getWorkDetails(this), this предполагается быть Person (который является статическим типом) и, следовательно, под названием соответствующей перегрузки.

Примечание. Использование this класса Employee сделало бы его типом Employee. Это можно проверить, перегружая work() в Employee следующим образом.

class Employee extends Person {
    ...

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

Ответ 3

Решение проблемы

В некоторых языках параметры разрешены для их динамического типа, но не в java. Компилятор уже определяет во время компиляции, когда ваш getWorkDetail(this); пойдет. this тип Person, поэтому getWorkDetail(Person e). В вашем конкретном случае решение совершенно очевидно. Как уже указывалось другими, вам необходимо переопределить getWorkDetail() в классе Employee.

Решение методов для их динамических типов параметров

Чтобы решить общую проблему разрешения типов параметров во время выполнения, следует избегать использования оператора instanceof, поскольку это обычно приводит к нечистому коду.

Если у вас есть два разных класса, решение, как указано выше, больше невозможно. В этих случаях вам придется использовать шаблон посетителя.

Рассмотрим следующие классы:

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

Что вы можете назвать так:

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

Следуя шаблону посетителя, звоните Animal.eat, вызывается Food.eatenBy, который реализуется как Meat и Vegetables. Эти классы будут называть более конкретным eatMeat или eatVegetables, который использует правильные (динамические) типы.

Ответ 4

Предпочтение вызова

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

Вывод будет ИНТ напечатан на консоли. Теперь вы прокомментируете первый метод test() и посмотрите, что будет выводить результат.

Это предпочтение hirarchey в примитивных типах данных. Теперь FooChild к производным типам объявляет класс FooChild следующим образом

class FooChild extends Foo {

}

и создать два новых метода в Foo

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

затем в основном методе попробуйте вызвать testChild как этот testChild(new FooChild()); ,

Ответ 5

getWorkDetail (это) не знает, что такое подклассы. вместо этого вызовите getWorkDetail.