Detailed explanation
Assuming the last two statements are inside a valid method or initializer, the code compiles and prints "Parent". The variable p has the declared type Parent, although it refers to a Child object. Because greet() is static, Java selects the method using the compile-time type of the expression before the dot. Therefore, p.greet() selects Parent.greet(). As a standalone ordinary Java source file, the snippet needs an execution context for those statements, typically a main method.
The declaration of greet() in Child hides the static method with the same signature in Parent; it does not override it. Static methods belong to classes rather than individual instances. An object can be used syntactically to access an accessible static method, but its runtime class does not determine which implementation runs. Writing Parent.greet() makes the intended class explicit and is preferable to p.greet(). Calling Child.greet() would print "Child", as would calling greet() through a variable declared as Child.
Under the hood, the compiler resolves this invocation to a static method reference, and the bytecode uses invokestatic rather than the invokevirtual instruction normally associated with these instance-method calls. There is no receiver-based dynamic dispatch for greet(). The new Child() expression still creates a Child object and performs its normal initialization, but that object's identity is irrelevant to selecting this static method. Even if p were null, p.greet() would still call Parent.greet() without a NullPointerException caused by a null receiver; no instance is needed for the static call.
Interviewers use this question to check whether you distinguish declared type, runtime type, method hiding, and overriding. If both greet() declarations were non-static instance methods, the same Parent reference to a Child object would invoke Child.greet() through dynamic dispatch. Adding @Override to the static Child.greet() instead causes a compilation error. A strong answer gives the output first, explains the binding rule, and briefly contrasts it with instance-method overriding.
Key points to remember
- With the executable statements placed in a valid context, the output is "Parent".
- Static method selection uses the expression's compile-time type, which is Parent here.
- Child.greet() hides Parent.greet(); it does not override it.
- The static invocation uses invokestatic and does not dispatch based on the runtime object.
- Prefer Parent.greet() or Child.greet() over accessing a static method through an object reference.
Common mistakes candidates make
- Predicting "Child" because p refers to a Child object.
- Calling the relationship overriding or expecting @Override to be valid on Child.greet().
- Claiming p.greet() is illegal merely because greet() is static; Java permits it, though tools may warn against it.
Likely follow-up questions
What changes if both greet() methods are non-static?
Child.greet() overrides Parent.greet(), and the call prints "Child". Instance-method dispatch uses the runtime object's class to select the overriding implementation.
What happens if Parent p = null; is followed by p.greet()?
It still prints "Parent" because the static call requires no receiver object. This does not make instance-method calls on null safe; those throw NullPointerException.
Can Child declare an instance greet() while Parent.greet() remains static?
No, an instance method cannot override an accessible static superclass method with the same signature. The reverse combination is also invalid: a static subclass method cannot hide an accessible instance superclass method.
Interview tip: State "Parent" first, identify p's declared type, and contrast static method hiding with instance-method overriding.
