Википедия определяет виртуальные методы как:
В объектно-ориентированном программировании виртуальная функция или виртуальный метод - это функция или метод, поведение которых может быть переопределено в наследующем классе функцией с подпись ame [для обеспечения полиморфного поведения].
Согласно определению, каждый нестатический метод в Java по умолчанию является виртуальным , кроме методов final и private. Метод, который не может быть унаследован для полиморфного поведения, является не виртуальным методом.
Статические методы в Java никогда нельзя переопределить; следовательно, бессмысленно объявлять статический метод как final в Java, потому что сами статические методы ведут себя так же, как final методы. Их можно просто скрыть в подклассах методами с той же сигнатурой. Очевидно, это так, потому что статические методы никогда не могут иметь полиморфного поведения: метод, который переопределяется, должен достигать полиморфизма, чего нельзя сказать о статических методах.
Из предыдущего абзаца можно сделать важный вывод. Все методы в C ++ по умолчанию являются статическими, потому что никакие методы в C ++ не могут вести себя полиморфно до тех пор, пока они не будут явно объявлены виртуальными в суперклассе. Напротив, все методы в Java, кроме final, static и private, по умолчанию являются виртуальными, потому что по умолчанию они имеют полиморфное поведение (нет необходимости явно объявлять методы как виртуальные в Java, и, следовательно, в Java нет такого ключевого слова, как "virtual" ).
Теперь давайте продемонстрируем, что переменные экземпляра (статические тоже) не могут вести себя полиморфно из следующего простого примера на Java.
class Super
{
public int a=5;
public int show()
{
System.out.print("Super method called a = ");
return a;
}
}
final class Child extends Super
{
public int a=6;
@Override
public int show()
{
System.out.print("Child method called a = ");
return a;
}
}
final public class Main
{
public static void main(String...args)
{
Super s = new Child();
Child c = new Child();
System.out.println("s.a = "+s.a);
System.out.println("c.a = "+c.a);
System.out.println(s.show());
System.out.println(c.show());
}
}
Результат, полученный с помощью приведенного выше фрагмента кода, выглядит следующим образом.
s.a = 5 c.a = 6 Дочерний метод с именем a = 6 Дочерний метод с именем a = 6
В этом примере оба вызова s.show ()
и c.show ()
выполняются для метода show ()
через переменные типа Super
и типа Child
соответственно вызывают метод show ()
в классе Child
.Это означает, что метод show ()
в классе Child
переопределяет метод show ()
в классе Super
, потому что оба из них иметь одинаковую подпись.
Это, однако, не может быть применено к переменной экземпляра a
, объявленной в обоих классах. В этом случае sa
будет относиться к a
в классе Super
, а display 5
и ca
будет относиться к на a
в классе Child
и отображение 6
означает, что a
в классе Child
просто скрывает (и не переопределяет, как это произошло с нестатическими методами) a
в классе Super
.
После этого долгого обсуждения остается только один вопрос. Почему переменные экземпляра (и остальные тоже) не переопределяются? В чем заключались особые причины для внедрения такого механизма? Были бы какие-то преимущества или недостатки, если бы они были отвергнуты?