Note that there are some explanatory texts on larger screens.

plurals
  1. POSelection of the Correct Method for Invocation
    primarykey
    data
    text
    <p>Take the following code:</p> <pre><code>public class Parent { public String doIt(Object o) { return "parent"; } } public class Child extends Parent { public String doIt(Object s) { return super.doIt(s) + ": " + "child"; } } public class Poly { public String makeItHappen() { Parent p = new Child(); return p.doIt("test"); } } </code></pre> <p>Due to the fact that Child.doIt() overrides Parent.doIt(), invoking Poly.makeItHappen() results in this being printed to the console:</p> <pre><code>parent: child </code></pre> <p>However, if I make a change in Child.doIt() to look like this:</p> <pre><code>public String doIt(String s) { return super.doIt(s) + ": " + "child"; } </code></pre> <p>Now Child.doIt() does not override Parent.doIt(). When you invoke Poly.makeItHappen(), you get this result:</p> <pre><code>parent </code></pre> <p>I'm a bit puzzled by this. The compile-time type of p is Parent so I certainly understand it finding Parent.doIt() as a potentially applicable method, but, given that the run-time type of p is Child, I'm not certain why Child.doIt() is not. Presuming that they are both identified as potentially applicable methods, I would expect Child.doIt(String) to be invoked over Parent.doIt(Object) as it is more specific.</p> <p>I've tried consulting the <a href="http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12" rel="nofollow">JLS</a> and found this bit:</p> <blockquote> <p>15.12.1 Compile-Time Step 1: Determine Class or Interface to Search</p> <p>...</p> <p>In all other cases, the qualified name has the form FieldName . Identifier; then the name of the method is the Identifier and the class or interface to search is the declared type T of the field named by the FieldName, if T is a class or interface type, or the upper bound of T if T is a type variable. </p> </blockquote> <p>To me, that says that the compile-time type of p would be used. This makes some sense when I see Parent.doIt(Object) being invoked while Child.doIt(String) is not. But it doesn't make sense in the terms of the polymorphic behavior noted when Child.doIt() properly overrides Parent.doIt() - in that case, both methods of Parent and Child are being analyzed to find potentially applicable methods so why not in the second case, as well?</p> <p>I know I'm missing something here, but I just can't figure out why I'm seeing the behavior that I am. If anyone could shed some insight into this, I'd appreciate it.</p> <p><strong>EDIT: FOUND THE ANSWER:</strong></p> <p>Thanks to Jack's response, I was able to find the answer within the JLS. The section of the JLS I referred to earlier was actually focused on finding the correct method at compile time and did not cover the process of invoking the correct method at run time. That portion of the process can be found the section of the JLS titled <a href="http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4" rel="nofollow">15.12.4 Runtime Evaluation of Method Invocation</a>.</p> <p>In there, I found this bit of text:</p> <blockquote> <p>Otherwise, the invocation mode is interface, virtual, or super, and overriding may occur. A dynamic method lookup is used. The dynamic lookup process starts from a class S, determined as follows:</p> </blockquote> <p>My invocation mode is virtual, so the above statement applies...</p> <blockquote> <p>If the invocation mode is interface or virtual, then S is initially the actual run-time class R of the target object.</p> <p>...</p> <p>The dynamic method lookup uses the following procedure to search class S, and then the superclasses of class S, as necessary, for method m.</p> </blockquote> <p>Okay, so this seemed very odd to me. According to this, the JVM is going to start looking for an applicable method in Child to invoke, which would lead me to believe that Child.doIt(String) would be invoked. But, reading on...</p> <blockquote> <p>Let X be the compile-time type of the target reference of the method invocation.</p> <p>...</p> <p>If class S contains a declaration for a non-abstract method named m with the same descriptor (same number of parameters, the same parameter types, and the same return type) required by the method invocation as determined at compile time (§15.12.3), then: </p> </blockquote> <p>The class "S", which would be Child, does indeed contain a method with the same descriptor as the method invocation determined at compile time (String "is a" Object, after all, so the descriptors are identical). It still seems to be like Child.doIt(String) should be getting invoked, but reading on...</p> <blockquote> <p>If the invocation mode is virtual, <strong>and the declaration in S overrides (§8.4.8.1) X.m</strong>, then the method declared in S is the method to be invoked, and the procedure terminates. </p> <p>...</p> <p>Otherwise, if S has a superclass, this same lookup procedure is performed recursively using the direct superclass of S in place of S; the method to be invoked is the result of the recursive invocation of this lookup procedure. </p> </blockquote> <p>The bit in bold is the really important part of this. As I mentioned, when I changed the method Child.doIt(), it no longer was overriding the doIt() method from Parent. So, even though the JVM is evaluating the Child.doIt() method as a potential candidate for invocation, it fails to be invoked because it does not override the method defined in X, which is Parent. I was really getting hung up because I thought the JVM was not even check Child.doIt as a potentially applicable method, and that didn't seem correct. Now I believe that the JVM <strong>is</strong> checking that method as a potentially applicable method, but then disregards it because it doesn't properly override the parent method. It's a situation in which I thought that the method from the subclass would be invoked, not because it overrides the parent class method, but because it is the most specific. In this case, however, that isn't true.</p> <p>The next line in the JLS simply explains that this procedure executes recursively over superclasses, leading Parent.doIt(Object) to be invoked.</p> <p>Intuitively, this made complete sense to me but I just couldn't wrap my head around how the JVM was actually executing this process. Of course, looking in the correct part of the JLS would have helped a great deal.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload