Какой перегруженный метод вызывается в 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
должна была превратиться в суперкласс. Однако это не то, что я нашел.
Очевидно, что в этот момент кода 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.