スーパークラスのオーバーロードされたメソッドを使用した基本的な継承状況があります。
_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(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");
}
}
オーバーロードの解決は、実行時ではなくコンパイル時に行われます。
したがって、getWorkDetails(this)
を呼び出すと、this
はPerson
(静的型)であると想定されるため、対応するオーバーロードと呼ばれます。
注:this
クラス内でEmployee
を使用すると、Employee
型になります。これを確認するには、Employee
のwork()
を次のようにオーバーロードします。
class Employee extends Person {
...
public void work() {
getWorkDetails(this); // This should print "This person is an Employee"
}
}
一部の言語では、パラメーターは動的タイプに解決されますが、Javaでは解決されません。コンパイラは、コンパイル時にgetWorkDetail(this);
がどこに移動するかをすでに決定しています。 this
はPerson
型であるため、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
が呼び出されます。これは、Meat
とVegetables
。これらのクラスは、より具体的なeatMeat
またはeatVegetables
メソッドを呼び出し、正しい(動的な)型を使用します。
コール設定
_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
を呼び出してみてください。