В общем, вы можете думать об этом как о:
Во время компиляции компилятор проверяет статическую привязку.
Во время выполнения проверяется динамический тип.
Например:
Class A{
public void function x(){ print("x"); }
}
Class B extends A{
public void function x(){ print("y"); }
public void function m(){ print("m"); }
}
public static void main(){
A a = new B();
a.x(); //1
a.m(); //2
((B)a).m(); //3
}
- в 1 скомпилируется, потому что статический тип a - это A, и у A есть функция с именем X, но во время выполнения будет распознан объект B, и напечатано 'y'
- в 2 произойдет ошибка компиляции, поскольку a имеет тип A, а в классе A нет функции с именем m.
- в 3 будет проверено, является ли это наследование B-> A допустимым, а затем, если в классе B есть функция с именем m.
* обратите внимание, что в последнем случае приведения компилятор проверяет только возможность наследования, а не наличие объекта B.
например:
A a = new A();
((B)a).m();
скомпилирует, но сгенерирует исключение времени выполнения.