Ich habe eine grundlegende Vererbungssituation mit einer überladenen Methode in der Superklasse.
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");
}
}
Die folgende Employee
-Klasse erweitert die obige Person
-Klasse:
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;
}
}
Die Hauptmethode erstellt einfach ein Employee
-Objekt (sowohl statischen als auch dynamischen Typ) und ruft .work()
auf:
public static void main(String[] args){
Employee e1 = new Employee("Manager1", 1976, "Female");
e1.work();
}
Dies endet mit dem Drucken
This person is not an Employee
Beim Durchschauen hatte ich gedacht, da sowohl der statische als auch der dynamische Typ des Objekts e1
Employee
ist, würde es die überladene Methode in Person aufrufen, die eine Employee
als Parameter verwendet. Da ich diesbezüglich eindeutig falsch bin, habe ich einen Debugger geöffnet, in dem der Verweis auf "this" in der Zeile getWorkDetail(this)
in der Person
-Klasse zu seiner Superklasse verwandelt werden muss. Dies habe ich jedoch nicht gefunden.
An dieser Stelle ist this
eindeutig ein Employee
-Objekt, jedoch hat es sich trotzdem entschieden, die überladene Methode getWorkDetail(Person p)
auszuführen. Kann jemand dieses Verhalten erklären?
Im Gegensatz zu Methodenüberschreibungen werden Methodenüberladungen basierend auf dem statischen Typ verknüpft. In diesem Fall weiß getWorkDetail(this)
in Person
nur den Typ Person
.
Das Überladen von Methoden ist nicht dafür ausgelegt, ein dynamisches Laufzeitverhalten bereitzustellen.
Um die dynamische Bindung zu nutzen, müssen Sie möglicherweise Ihren Code umgestalten, um die Methoden zu überschreiben.
public static void main(String[] args) throws IOException {
new Employee("Manager1", 1976, "Female").getWorkDetail();
new Person("Manager1", 1976, "Female").getWorkDetail();
}
Und ändern Sie das Verhalten basierend auf der Implementierung von Klassen. Selbstverständlich können Sie Methoden überladen, sofern Sie auch die überladenen Methoden überschreiben, falls dies erforderlich ist.
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");
}
}
Die Überlastauflösung erfolgt während der Kompilierzeit, nicht zur Laufzeit.
Wenn Sie also getWorkDetails(this)
aufrufen, wird angenommen, dass this
eine Person
ist (was der statische Typ ist) und daher die entsprechende Überladung genannt wird.
Anmerkung: Die Verwendung von this
in der Klasse Employee
hätte einen Employee
-Typ zur Folge. Sie können dies überprüfen, indem Sie work()
in Employee
wie folgt überladen.
class Employee extends Person {
...
public void work() {
getWorkDetails(this); // This should print "This person is an Employee"
}
}
In einigen Sprachen werden Parameter in ihren dynamischen Typ aufgelöst, jedoch nicht in Java. Der Compiler bestimmt bereits zur Kompilierzeit, wohin Ihre getWorkDetail(this);
gehen soll. this
ist vom Typ Person
, daher wird getWorkDetail(Person e)
aufgerufen. In Ihrem speziellen Fall liegt die Lösung auf der Hand. Wie bereits erwähnt, müssen Sie getWorkDetail()
in der Employee
-Klasse überschreiben.
Um das allgemeine Problem der Auflösung von Parametertypen zur Laufzeit zu lösen, sollte der Operator instanceof
vermieden werden, da dies in der Regel zu unsauberem Code führt.
Wenn Sie zwei verschiedene Klassen haben, ist eine so einfache Lösung wie oben beschrieben nicht mehr möglich. In diesen Fällen müssen Sie das Besuchermuster verwenden.
Betrachten Sie die folgenden Klassen:
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);
}
}
Das kannst du so nennen:
Animal animal = new Shark();
Food someMeat = new Meat();
Food someVegetables= new Vegetables();
animal.eat(someMeat); // prints "Tasty meat!"
animal.eat(someVegetables); // prints "Yuck!"
Nach dem visitor-Muster wird beim Aufruf von Animal.eat
Food.eatenBy
aufgerufen, das von Meat
und Vegetables
implementiert wird. Diese Klassen rufen die spezifischere eatMeat
- oder eatVegetables
-Methode auf, die die richtigen (dynamischen) Typen verwendet.
Anrufpräferenz
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);
}
}
Die Ausgabe wird auf der Konsole int gedruckt. Jetzt kommentieren Sie die erste test()
-Methode und sehen, welche Ausgabe kommt.
Dies ist die Präferenzhirarchey in primitiven Datentypen. Wenn Sie jetzt zu abgeleiteten Typen kommen, deklarieren Sie eine Klasse FooChild
wie folgt
class FooChild extends Foo {
}
und erstellen Sie zwei neue Methoden in Foo
like
static void testChild(Foo foo) { System.out.println("Foo"); }
static void testChild(FooChild fooChild) { System.out.println("FooChild"); }
dann versuchen Sie in der Hauptmethode, testChild
wie folgt testChild(new FooChild());
aufzurufen.