Переопределение статического метода в Java
В Java нельзя переопределять статические методы. Но почему работает следующий код:
public class Runner {
public static void main(String[] args) {
B.fun();
}
}
class A {
public static void fun() {
System.out.println("A");
}
}
class B extends A {
public static void fun() {
System.out.println("B");
}
}
На печать выводится "B". Все выглядит так, как будто это переопределение. Потому что при перегрузке я должна была бы что-то изменить в сигнатуре метода. Но нет, я ничего не меняла.
Аннотацию "override" не могу поставить над методом.
Ответы (3 шт):
... работает следующий код:
На печать выводится "B".
Потому что Вы вызываете fun так, что компилятору видно, что метод вызывается в контексте класса B.
Аннотацию "override" не могу поставить над методом.
Потому что статические методы - невиртуальные.
Начнем по порядку
Переопределение метода:
Переопределение метода (Method overriding) это возможность языка позволяющая подклассу или дочернему элементу классу обеспечивать специфическую реализацию метода, который уже реализовали в одном из супер-классов или родительском классе. Переопределение выглядит так:
public class App {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
dog.voice(); // Вывод: Гав
cat.voice(); // Вывод: Мяу
}
}
class Animal { // родительский класс
public void voice() {
System.out.print("Голос животного");
}
}
class Dog extends Animal { // класс-наследник
@Override // Переопределение метода voice() унаследованного от класса Animal в классе-наследнике Dog
public void voice() {
System.out.print("Гав");
}
}
class Cat extends Animal { // класс-наследник
@Override // Переопределение метода voice() унаследованного от класса Animal в классе-наследнике Cat
public void voice() {
System.out.print("Мяу");
}
}
Переопределенный метод должен иметь тот же модификатор доступа что его родитель, принимать аргументы как его родитель, и иметь возвращаемый тип такой же как у его родителя
Т.е.
public class App {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
dog.voice("Шарик", 10); // Вывод: Гав
cat.voice("не Шарик", 7); // Вывод: Мяу
}
}
class Animal {
// Родительский метод имеет возвращаемый тип String. Принимает два параметра, типа String и int
public String voice(String animal_name, int animal_age) {
System.out.print("Гав");
return animal_name;
}
}
class Dog extends Animal{
// Переопределенный метод должен как его родитель, иметь возвращаемый тип String, и принимать параметры как его родитель
@Override
public String voice(String dog_name, int dog_age) {
System.out.print("Гав");
return dog_name;
}
}
class Cat extends Animal {
@Override
public String voice(String cat_name, int cat_age) {
System.out.print("Мяу");
return cat_name;
}
}
Сокрытие метода:
Сокрытие методов, грубо говоря, представляет из себя "перекрытие" методом текущего класса, метода родительского класса. Выглядит это следующим образом:
public class App {
public static void main(String[] args) {
Dog.voice(); // Вывод: Гав
}
}
class Animal {
public static void voice() {
System.out.print("звук");
}
}
class Dog extends Animal {
// Метод определенный в классе-наследнике идентичный по сигнатуре с методом родительского класса
public static void voice() {
System.out.print("Гав");
}
}
Сигнатуры методов этих двух классов идентичны, т.е. в классе родителе Animal есть public static void voice() и в классе-наследнике Dog есть public static void voice(). Следовательно вызов Dog.voice() будет вызывать метод определенный в классе Dog
Важно отметить, что перекрытие требует таких же правил как и переопределение метода.
Переопределенный метод должен иметь тот же модификатор доступа что его родитель, принимать аргументы как его родитель, и иметь возвращаемый тип такой же как у его родителя
т.е.
public class App {
public static void main(String[] args) {
Dog.voice(); // Вывод: Гав
Dog.voice("Шарик"); // Вывод: Шарик говорит гав
}
}
class Animal {
public static void voice() {
System.out.print("звук");
}
}
class Dog extends Animal {
public static void voice() { // Метод перекрывающий унаследованный метод
System.out.print("Гав");
}
public static void voice(String name) { // Отдельный метод класса Dog
System.out.print(name + " говорит гав");
}
}
Рассмотрим использование таких методов в своих классах
- Использование без схожего по сигнатуре метода
public class App {
public static void main(String[] args) {
Dog.voice(); // Вывод: звук
}
}
class Animal {
public static void voice() {
System.out.print("звук");
}
}
class Dog extends Animal {
public static void dog_voice() {
voice();
}
}
Поскольку в классе-наследнике нет реализованного схожего по сигнатуре с родительским классом метода, то используется метод унаследованный от родительского класса
- Использование схожего по сигнатуре метода
public class App {
public static void main(String[] args) {
Dog.dog_voice(); // Вывод: гав
}
}
class Animal {
public static void voice() {
System.out.print("звук");
}
}
class Dog extends Animal {
public static void voice() {
System.out.print("Гав");
}
public static void dog_voice() {
voice();
}
}
Поскольку теперь у класса Dog появился "свой метод" voice() он перекрывает метод унаследованный от Animal
- Использование метода унаследованного от родителя, когда есть метод перекрывающий его
public class App {
public static void main(String[] args) {
Dog.dog_voice(); // Вывод: Гавзвук
}
}
class Animal {
public static void voice() {
System.out.print("звук");
}
}
class Dog extends Animal {
public static void voice() { // Метод перекрывающий унаследованный метод
System.out.print("Гав");
}
public static void dog_voice() {
voice(); // Вызов метода voice определенного в классе Dog;
Animal.voice(); // Вызов метода voice определенного в классе Animal
}
}
Хочу дополнить тем, что перекрытие работает не только с методами класса, но и с его полями. Небольшой пример:
public class App {
public static void main(String[] args) {
Dog.animal(); // Вывод: Имя этого животного Собакен
}
}
class Animal {
public static String animal_name = "Нет имени, просто животное";
public static void animal() {
System.out.print("Имя этого животного " + animal_name);
}
}
class Dog extends Animal {
public static String animal_name = "Собакен"; // Перекрывает поле родительского класса Animal
public static void animal() {
System.out.print("Имя этого животного " + animal_name);
}
}
ПЕРЕОПРЕДЕЛЕНИЕ метода - это когда ты создала обьект b от подкласса B, и вызвала от объекта метод, а метод запустился не от суперкласса, а от подкласса которому принадлежит объект b. Пример:
B b = new B();
b.fun();
Что же касается твоего кода, то ты НЕ создавала обьект, а поэтому ПЕРЕОПРЕДЕЛЕНИЕ объекта не имело место быть. В твоем коде есть просто прямое обращение к методу с указанием что он лежит в классе В:
B.fun();
Забавно, что переопределение статического метода ВОЗМОЖНО. НО нужно помнить что статический метод именно для того и создавали что бы вызывать его БЕЗ создания/указания объекта. Поэтому программисты договорились НЕ писать:
b.fun();
Программисты договорились вызывать статические методы именно как у тебя в коде и написанно (БЕЗ создания обьекта):
B.fun();