首页 文章

在Java中调用哪个重载方法

提问于
浏览
56

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

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

这最终打印

此人不是员工

通过这个看,我原以为由于对象 e1 的静态和动态类型都是 Employee ,它会调用Person中的重载方法,该方法将 Employee 作为参数 . 由于我对此明显错误,我打开了一个调试器,假设在 Person 类的 getWorkDetail(this) 行的"this"的引用必须已经变形为它的超类 . 然而,这不是我发现的 .

screenshot

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

5 回答

  • 23

    与方法覆盖不同,方法重载基于静态类型链接 . 在这种情况下, 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");
        }
    }
    
  • 7

    重载解析在编译期间发生,而不是在运行时 .

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

    注意:在 Employee class中使用 this 会使它成为 Employee 类型 . 您可以通过在 Employee 中重载 work() 来验证这一点 .

    class Employee extends Person {
        ...
    
        public void work() {
            getWorkDetails(this); // This should print "This person is an Employee"
        }
    }
    
  • -4

    特定于问题的解决方案

    在某些语言中,参数被解析为其动态类型,但不是在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!"
    

    visitor pattern 之后调用 Animal.eat 将调用 Food.eatenBy ,它由 MeatVegetables 实现 . 这些类将调用更具体的 eatMeateatVegetables 方法,该方法使用正确的(动态)类型 .

  • 69

    Call preference

    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() 方法,看看输出的是什么 .

    这是原始数据类型中的首选hirarchey . 现在来到派生类型会像这样声明一个类 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 .

  • 0

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

相关问题