|
This is about runtime binding vs static binding, not static objects or static members. There is still a "this", but the code that will run is known up front -- "ahead of time". C++ went through this 25+ years ago: runtime binding, what in C++ is virtual functions, is a niche technique. Most C++ programs don't use it at all, or use it in only one or two spots. When it is the right thing, it makes the work convenient, but it really is just a dance with function pointers. In C++, templates do the heavy lifting. Java never offered any other support for organizing programs, so inheritance and virtual functions have been your go-to for everything, no matter how bad the fit. In a static call there is only one bit of code to run, and it never changes over the life of the program. Just like almost everything, really, except here your runtime knows up front. It was always a dumb choice to make member functions default to virtual semantics, when they almost always don't need it, and it just costs performance to no purpose. That is what comes out of treating language design as a marketing exercise: Java's designers really (and openly) cared less than nothing about object-oriented programming. They thought people really ought to be coding Lisp. Forcing runtime binding was a way to sneak in something a little bit lispy, and maybe get people used to production code running no faster than Lisp. |
If you actually don't need it (i. e., your hot methods are never overridden), then the JIT will trivially compile those "virtual" method calls as non-virtual ones. It has all the information it needs, since it knows what classes are loaded. It can invalidate its code if necessary, if you load more classes that do add overrides. So no, it does not cost performance at the call site. It does cause the compiler to do work, but nothing fancy.