Only virtual methods - these are really parts of the class instance - you can imagine that all the other methods are simply a static method that takes this argument.
So, when you rewrite the code to show this more explicitly (note that I am using C #, Java is a little different, but basically the same principle applies):
class A { private static void sayA(A @this) { "Hi from A".Dump(); } public static void Test() { sayA(new B()); } } class B : A { }
This makes it much more obvious what the scope of the method is - it does not bind to the type instance only to the type itself. Therefore, regardless of whether you call new B().sayA() or new A().sayA() , the same method is called, only with a different argument.
On the other hand, if you write code from B , the sayA method sayA simply unavailable - again, A.sayA(...) clearly unavailable because it is closed to A
This changes when you make the method public or secure, and virtual (by default in Java). In this case, the method being called depends on the type of execution to which you are calling the method, and not on the type of compilation time or on the scope from which you are calling it. This means that new B().VirtualA() will call the same method as ((A)new B()).VirtualA() , even if the compile time types are different. And, of course, new A().VirtualA() may (or may not - depending on whether B redefines VirtualA ) another method.
source share