Какой перегруженный метод вызывается в Java - PullRequest
0 голосов
/ 25 апреля 2018

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

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). Кто-нибудь может объяснить это поведение?

Ответы [ 5 ]

0 голосов
/ 25 апреля 2018

Решение проблемы, специфичной для проблемы

В некоторых языках параметры разрешаются в их динамическом типе, но не в 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);
    }
}

, которые вы можете назвать как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, который использует правильные (динамические) типы.

0 голосов
/ 25 апреля 2018

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

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

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

Это предпочтение хирарчей в примитивных типах данных.Теперь, переходя к производным типам, объявите класс 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());.

0 голосов
/ 25 апреля 2018

В отличие от переопределений методов, перегрузки методов связаны на основе статического типа. И в этом случае 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");
    }
}
0 голосов
/ 25 апреля 2018

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

Итак, когда вы вызываете 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"
    }
}
0 голосов
/ 25 апреля 2018

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

...