web-dev-qa-db-ja.com

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

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"への参照がスーパークラスにモーフィングされていると仮定して、デバッガーを開きました。しかし、これは私が見つけたものではありません。

screenshot

明らかにコードのこの時点でthisEmployeeオブジェクトですが、それでもオーバーロードされたメソッドgetWorkDetail(Person p)を実行することを選択しました。誰でもこの動作を説明できますか?

57
Eric S

メソッドのオーバーライドとは異なり、メソッドのオーバーロードは静的型に基づいてリンクされます。この場合、PersongetWorkDetail(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");
    }
}
70
ernest_k

オーバーロードの解決は、実行時ではなくコンパイル時に行われます。

したがって、getWorkDetails(this)を呼び出すと、thisPerson(静的型)であると想定されるため、対応するオーバーロードと呼ばれます。

注:thisクラス内でEmployeeを使用すると、Employee型になります。これを確認するには、Employeework()を次のようにオーバーロードします。

class Employee extends Person {
    ...

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

問題固有の解決策

一部の言語では、パラメーターは動的タイプに解決されますが、Javaでは解決されません。コンパイラは、コンパイル時にgetWorkDetail(this);がどこに移動するかをすでに決定しています。 thisPerson型であるため、getWorkDetail(Person e)が呼び出されます。特定のケースでは、ソリューションは非常に明白です。他の人がすでに指摘しているように、EmployeeクラスのgetWorkDetail()をオーバーライドする必要があります。

メソッドを動的パラメータータイプに解決する

実行時にパラメーターの型を解決するという一般的な問題を解決するには、instanceof演算子の使用を避ける必要があります。

2つの異なるクラスがある場合、上記のような単純な解決策はもはや不可能です。これらの場合、 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。これらのクラスは、より具体的なeatMeatまたはeatVegetablesメソッドを呼び出し、正しい(動的な)型を使用します。

7
Lonely Neuron

コール設定

_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のような2つの新しいメソッドを作成します

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

次に、メインメソッドで、このtestChild(new FooChild());のようにtestChildを呼び出してみてください。

0
Arun Sudhakaran