Single Dispatch
class Parent {
void print(String a) { log.info("Parent - String"); } void print(Object a) { log.info("Parent - Object"); } } class Child extends Parent { void print(String a) { log.info("Child - String"); } void print(Object a) { log.info("Child - Object"); } }
String string = "";
Object stringObject = string; // What gets printed? Child child = new Child(); child.print(string); child.print(stringObject); Parent parent = new Child(); parent.print(string); parent.print(stringObject);
child.print(string); // Prints: "Child - String"
child.print(stringObject); // Prints: "Child - Object" parent.print(string); // Prints: "Child - String" parent.print(stringObject); // Prints: "Child - Object"
Method is called depends on the type of instance of "real" instance types, rather than a "statement."
Java does not support double dispatch, therefore, at the time of treatment parameters, it is important to "declare" type parameters, rather than the "real" type.
It is a term of double the scheduling process of a method at runtime call receiver type and parameter selection. (You can use the visitor pattern to achieve the same effect)
Hidden Override
class Parent {
void print(Object a) { log.info("Parent - Object"); } } class Child extends Parent { void print(String a) { log.info("Child - String"); } }
String string = "";
Parent parent = new Child(); parent.print(string);
parent.print(string); // Prints: "Parent - Object"
Before checking subclass covering, Java will choose the first method to be called. In this case, the instance type is declared
Parent
,Parent
only matching methodParent :: print(Object)
. When Java then checksParent :: print(Object)
when any potential coverage, it could not find any coverage, so this is a method of execution.
Exposed Override
class Parent {
void print(Object a) { log.info("Parent - Object!"); } void print(String a) { throw new RuntimeException(); } } class Child extends Parent { void print(String a) { log.info("Child - String!"); } }
String string = "";
Parent parent = new Child(); parent.print(string);
parent.print(string); // Prints: "Child - String!"
Ambiguous Parameter
class Foo {
void print(Cloneable a) { log.info("I am cloneable!"); } void print(Map a) { log.info("I am Map!"); } }
HashMap cloneableMap = new HashMap();
Cloneable cloneable = cloneableMap; Map map = cloneableMap; // What gets printed? Foo foo = new Foo(); foo.print(map); foo.print(cloneable); foo.print(cloneableMap);
foo.print(map); // Prints: "I am Map!"
foo.print(cloneable); // Prints: "I am cloneable!" foo.print(cloneableMap); // Does not compile
Not compile because there are multiple methods are equally effective for a given parameter.
Multiple Inheritance – Interfaces
interface Father {
default void print() { log.info("I am Father!"); } } interface Mother { default void print() { log.info("I am Mother!"); } } class Child implements Father, Mother {}
new Child().print();
Not compile because the default method of conflict of Father and Mother
Multiple Inheritance – Class and Interface
class ParentClass {
void print() { log.info("I am a class!"); } } interface ParentInterface { default void print() { log.info("I am an interface!"); } } class Child extends ParentClass implements ParentInterface {}
new Child().print(); // Prints: "I am a class!"
If there is a conflict between the classes and interfaces inherit, then the class win.
Transitive Override
class Parent {
void print() { foo(); }
void foo() { log.info("I am Parent!"); } } class Child extends Parent { void foo() { log.info("I am Child!"); } }
new Child().print(); // Prints: "I am Child!"
Coverage will take effect on the delivery method call. If the method is overridden, then
Parent :: print
the call is coveredfoo()
version.
Private Override
class Parent {
void print() { foo(); }
private void foo() { log.info("I am Parent!"); } } class Child extends Parent { void foo() { log.info("I am Child!"); } }
new Child().print(); // Prints: "I am Parent!"
Parent.foo()
It is declared as private. Therefore, whenParent.print()
invokedfoo()
, it is hard-codedParent.foo()
. Whether or not there is a subclassfoo()
of the other implementations or call theprint()
actual type of instance.
Static Overrides
class Parent {
static void print() { log.info("I am Parent!"); } } class Child extends Parent { static void print() { log.info("I am Child!"); } }
Child child = new Child();
Parent parent = child; parent.print(); // Prints: "I am Parent!" child.print(); // Prints: "I am Child!"
Java does not allow rewrite static method. If the parent and child classes are defined in the same static method, the actual type of the instance does not matter. Only the type declarations calling for determining which of the two methods.
Static Linking
class Parent {
void print() { staticMethod(); instanceMethod(); }
static void staticMethod() { log.info("Parent::staticMethod"); } void instanceMethod() { log.info("Parent::instanceMethod"); } } class Child extends Parent { static void staticMethod() { log.info("Child::staticMethod"); } void instanceMethod() { log.info("Child::instanceMethod"); } }
Child child = new Child();
child.print();
Parent::staticMethod
Child::instanceMethod
For instance method, even if the caller in the parent, will take effect cover. However, static methods, even if the type of the variable is declared Child, due in the middle of
print()
the method, also calledParent :: staticMethod
.
Wrapping up
- Always use
@Override
comment tags to cover all methods - Always use a class reference rather than a reference to an instance to call the static method
- IDE or lint error alert is provided to enforce the above and other code detection
- Instead of using a combination of inheritance