Java中调用了哪个重载方法

Posted

技术标签:

【中文标题】Java中调用了哪个重载方法【英文标题】:Which Overloaded Method is Called in Java 【发布时间】:2018-10-05 10:44:17 【问题描述】:

我有一个基本的继承情况,超类中有一个重载的方法。

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;
    

main 方法只是简单地创建一个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,它会调用以Employee 作为参数的Person 中的重载方法。由于我对此显然错了,我打开了一个调试器,假设在Person 类中的getWorkDetail(this) 行中对“this”的引用必须已经变形为它的超类。然而,这不是我发现的。

显然此时代码this 是一个Employee 对象,但是它仍然选择执行重载方法getWorkDetail(Person p)。谁能解释这种行为?

【问题讨论】:

恕我直言,父类不应该对子类一无所知。我的意思是Person 类中没有getWorkDetail(Employee e) 方法的位置... @zlakad 我认为这些功能应该是静态的? @StarWeaver,静态与否,这个特定问题有什么区别?Person中的this是静态类型Person 正如@zlakad 所暗示的,这个类结构的结构很差。 this 是一种隐含的非静态方法。在这里将this 传递给成员方法确实没有任何意义。 【参考方案1】:

与方法重载不同,方法重载是基于静态类型链接的。而在这种情况下,Person 中的getWorkDetail(this) 只知道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");
    

【讨论】:

@Ernest Kiwele 谢谢!我不知道 Java 不为方法重载提供动态行为。这样就搞定了。 您可能还需要注意,JVM 首先查找具有您指定的签名的非重写方法,然后再查找与您的签名匹配的重写方法。但理想情况下,您希望利用 Java 的继承机制来重构您的代码,就像 @Ernest 展示的那样【参考方案2】:

重载解决发生在编译时,而不是运行时。

因此,当您调用 getWorkDetails(this) 时,this 被假定为 Person(这是静态类型),因此称为相应的重载。

注意:在 Employee 类中使用 this 会使其成为 Employee 类型。您可以像这样在Employee 中重载work() 来验证这一点。

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)。在您的具体情况下,解决方案非常明显。正如其他人已经指出的那样,您需要在 Employee 类中覆盖 getWorkDetail()

将方法解析为其动态参数类型

要解决在运行时解析参数类型的一般问题,应避免使用instanceof 运算符,因为它通常会导致代码不干净。

如果您有两个不同的类,那么上述简单的解决方案将不再可能。在这些情况下,您必须使用visitor pattern

考虑以下类:

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 实现。这些类将调用更具体的eatMeateatVegetables 方法,它们使用正确的(动态)类型。

【讨论】:

【参考方案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);
    

输出将 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"); 

然后在 main 方法中尝试像这样testChild(new FooChild()); 调用testChild

【讨论】:

【参考方案5】:

getWorkDetail(this) 不知道子类是什么。改为调用 getWorkDetail。

【讨论】:

以上是关于Java中调用了哪个重载方法的主要内容,如果未能解决你的问题,请参考以下文章

JAVA方法重载

JAVA基础之重载,覆盖/重写,多态

java中重载,继承,重写和多态的区别

在 Java 中,哪个重载会被选为 null?

Java重写重载的区别

Java-重载重写(冷静分析)