Статические методы в родительском классе и наследнике
Есть код:
public class Coffee {
String name="Кофе";
public static void buyCoffee(Coffee coffee) {
System.out.println("Вы купили " + coffee.name);
}
}
public class Latte extends Coffee{
String name = "Латте";
public static void buyCoffee(Coffee coffee) {
System.out.println("Вы купили " + coffee.name);
}
}
import static filosofia.java.p212.inheritance.static_method.Latte.buyCoffee;
public class Main {
public static void main(String[] args) {
Latte latte = new Latte();
buyCoffee(latte);
}
}
Который выводит следующий результат
Вы купили Кофе
Я знаю, что статические методы не поддерживают полиморфное поведение, поэтому вывод Вы купили Кофе для объекта Coffee coffee = new Latte() было бы ожидаемо.
Но почему на объект Latte latte = new Latte() java реагирует как на объект типа Coffee не понятно. Т.е. при отсутствии созданного экземпляра класса Coffee программа все равно обратится к его полю. Как такое может быть?
Ответы (2 шт):
Определение перекрывающихся переменных происходит на основе типа, который имеет переменная, а не реальный тип объекта. Это отличается от методов, которые наоборот определяются типом реального объекта, а не типом переменной.
Поскольку у вас стоит изменение типа в статическом методе с Latte на Coffee, то будет произведено обращение к родительскому классу.
public class Parent {
String name = "Parent";
}
public class Child extends Parent {
String name = "Child";
}
Parent parent = new Parent();
Child child = new Child();
Parent childWithTypeParent = new Child();
System.out.println(parent.name); // Parent
System.out.println(child.name); // Child
System.out.println(childWithTypeParent.name); // Parent
Следовательно, поменяв код на
public class Latte extends Coffee{
String name = "Латте";
public static void buyCoffee(Latte latte) {
System.out.println("Вы купили " + latte.name);
}
}
Вы получите вывод "Вы купили Латте".
Поля классов нельзя "переопределить" (override), только методы экземпляра.
Поскольку и в классе родителя Coffee, и в классе-потомке Latte определены поля с одинаковым именем name, при прямом обращении к такому полю (без виртуального геттера) будет возвращаться значение, соответствующее типу ссылки на данный экземпляр. То есть, переменная name будет браться из того класса, тип которого указан в параметре метода, а не из типа своей ссылки.
Т.е. при отсутствии созданного экземпляра класса
Coffeeпрограмма все равно обратится к его полю
При вызове конструктора new Latte() будет вызван конструктор родительского класса по умолчанию new Coffee(), который "создаст" поле родителя, к которому будет доступ через тип ссылки:
new Latte()
-> new Coffee()
{name = "Coffee"}
{name = "Latte"}
Для вывода названия Latte потребуется "перегрузить" (overload) метод, чтобы он принимал аргумент типа Latte:
class Coffee {
String name = "Кофе";
public static void buyCoffee(Coffee coffee) {
System.out.println("Coffee: Вы купили " + coffee.name);
}
}
class Latte extends Coffee {
String name = "Латте"; // скрывает поле в классе-родителе
public static void buyCoffee(Coffee coffee) {
System.out.println("Latte: coffee: Вы купили " + coffee.name);
}
public static void buyCoffee(Latte latte) {
System.out.println("Latte: latte: Вы купили " + latte.name);
}
}
Тест:
Coffee coffee = new Coffee();
Latte.buyCoffee(coffee); // buyCoffee(Coffee)
Coffee coffeeLatte = new Latte();
Latte.buyCoffee(coffeeLatte); // buyCoffee(Coffee)
Latte latte = new Latte();
Latte.buyCoffee(latte); // buyCoffee(Latte)
Результат
Latte: coffee: Вы купили Кофе
Latte: coffee: Вы купили Кофе
Latte: latte: Вы купили Латте