public class Inheritance {
 
    public static void main(String[] args) {
        Parent justParent = new Parent();
        Parent childDisguisedAsParent = new Child("Bob");
        Child actuallyAChild = new Child("Alice");
 
        System.out.println("=== Just Parent ===");
        System.out.println("Accessing 'name' property:       " + justParent.name);
        System.out.println("Accessing 'familyName' property: " + justParent.familyName);
        System.out.println("Calling 'who()' method:          " + justParent.who());
        System.out.println();
 
        System.out.println("=== Child Disguised As Parent ===");
		// Accesses parent's name property due to being of type (Parent)
        System.out.println("Accessing 'name' property:                      " + childDisguisedAsParent.name);
		
		// Down-casting to (Child) allows us to access the child's name property
        System.out.println("Accessing 'name' property (Down Cast):          " + ((Child) childDisguisedAsParent).name);
		
		// Accesses parent's name property 
        System.out.println("Accessing 'familyName' property:                " + childDisguisedAsParent.familyName);
		
		// Call's the method defined in Child despite being of type (Parent), because the real type is (Child) which overrides 'who()'
        System.out.println("Calling 'who()' method:                         " + childDisguisedAsParent.who()); 
 
		// 'parentsWho()' doesn't exist in (Parent), so down-casting is needed to be able to call the method
        System.out.println("Calling 'parentsWho()' method (MUST Down Cast): " + ((Child)childDisguisedAsParent).parentsWho()); 
        System.out.println();
 
        System.out.println("=== Actually A Child ===");
        System.out.println("Accessing 'name' property:           " + actuallyAChild.name);
        System.out.println("Accessing 'name' property (Up Cast): " + ((Parent) actuallyAChild).name);
        System.out.println("Accessing 'familyName' property:     " + actuallyAChild.familyName);
        System.out.println("Calling 'who()' method:              " + actuallyAChild.who());
        System.out.println("Calling 'parentsWho()' method:       " + ((Child)childDisguisedAsParent).parentsWho());
        System.out.println();
    }
 
    public static class Parent {
        public final String name = "dad";
        public final String familyName = "Kong";
 
        public String who() {
            return "I am the parent! My name is " + name;
        }
    }
 
    public static class Child extends Parent {
        public final String name;
 
        public Child(String name) {
            super();
            this.name = name;
        }
 
        public String who() {
            return "I am the child! My name is " + name;
        }
 
        public String parentsWho() {
            return super.who();
        }
    }
 
}